Le guide essentiel du nouveau type de données JavaScript : BigInt
Publié: 2022-03-10Number
ne peut pas représenter en toute sécurité des valeurs entières supérieures à 2 53 . Cette limitation a obligé les développeurs à utiliser des solutions de contournement inefficaces et des bibliothèques tierces. BigInt
est un nouveau type de données destiné à résoudre ce problème. Le type de données BigInt
vise à permettre aux programmeurs JavaScript de représenter des valeurs entières supérieures à la plage prise en charge par le type de données Number
. La capacité de représenter des nombres entiers avec une précision arbitraire est particulièrement importante lors de l'exécution d'opérations mathématiques sur de grands nombres entiers. Avec BigInt
, le débordement d'entier ne sera plus un problème.
De plus, vous pouvez travailler en toute sécurité avec des horodatages haute résolution, des ID entiers volumineux, etc., sans avoir à utiliser de solution de contournement. BigInt
est actuellement une proposition de stade 3. Une fois ajouté à la spécification, il deviendra le deuxième type de données numériques en JavaScript, ce qui portera à huit le nombre total de types de données pris en charge :
- booléen
- Nul
- Indéfini
- Nombre
- BigInt
- Chaîne de caractères
- symbole
- Objet
Dans cet article, nous examinerons BigInt
et verrons comment il peut aider à surmonter les limitations du type Number
en JavaScript.
Le problème
L'absence d'un type entier explicite en JavaScript est souvent déroutante pour les programmeurs venant d'autres langages. De nombreux langages de programmation prennent en charge plusieurs types numériques tels que float, double, integer et bignum, mais ce n'est pas le cas avec JavaScript. En JavaScript, tous les nombres sont représentés au format à virgule flottante 64 bits double précision tel que défini par la norme IEEE 754-2008.
Selon cette norme, les très grands nombres entiers qui ne peuvent pas être représentés exactement sont automatiquement arrondis. Pour être précis, le type Number
en JavaScript ne peut représenter en toute sécurité que des entiers compris entre -9007199254740991 (-(2 53 -1)) et 9007199254740991 (2 53 -1). Toute valeur entière qui tombe en dehors de cette plage peut perdre en précision.
Cela peut être facilement examiné en exécutant le code suivant :
console.log(9999999999999999); // → 10000000000000000
Cet entier est supérieur au plus grand nombre que JavaScript peut représenter de manière fiable avec la primitive Number
. Elle est donc arrondie. Un arrondi inattendu peut compromettre la fiabilité et la sécurité d'un programme. Voici un autre exemple :
// notice the last digits 9007199254740992 === 9007199254740993; // → true
JavaScript fournit la constante Number.MAX_SAFE_INTEGER
qui vous permet d'obtenir rapidement l'entier sûr maximal en JavaScript. De même, vous pouvez obtenir l'entier sûr minimum en utilisant la constante Number.MIN_SAFE_INTEGER
:
const minInt = Number.MIN_SAFE_INTEGER; console.log(minInt); // → -9007199254740991 console.log(minInt - 5); // → -9007199254740996 // notice how this outputs the same value as above console.log(minInt - 4); // → -9007199254740996
La solution
Pour contourner ces limitations, certains développeurs JavaScript représentent de grands entiers à l'aide du type String
. L'API Twitter, par exemple, ajoute une version chaîne des ID aux objets lors de la réponse avec JSON. De plus, un certain nombre de bibliothèques telles que bignumber.js ont été développées pour faciliter le travail avec de grands nombres entiers.
Avec BigInt
, les applications n'ont plus besoin d'une solution de contournement ou d'une bibliothèque pour représenter en toute sécurité des entiers au-delà Number.MAX_SAFE_INTEGER
et Number.Min_SAFE_INTEGER
. Les opérations arithmétiques sur les grands entiers peuvent désormais être effectuées en JavaScript standard sans risquer de perte de précision. L'avantage supplémentaire de l'utilisation d'un type de données natif par rapport à une bibliothèque tierce est une meilleure performance d'exécution.
Pour créer un BigInt
, ajoutez simplement n
à la fin d'un entier. Comparer:
console.log(9007199254740995n); // → 9007199254740995n console.log(9007199254740995); // → 9007199254740996
Vous pouvez également appeler le constructeur BigInt()
:
BigInt("9007199254740995"); // → 9007199254740995n
Les littéraux BigInt
peuvent également être écrits en notation binaire, octale ou hexadécimale :
// binary console.log(0b100000000000000000000000000000000000000000000000000011n); // → 9007199254740995n // hex console.log(0x20000000000003n); // → 9007199254740995n // octal console.log(0o400000000000000003n); // → 9007199254740995n // note that legacy octal syntax is not supported console.log(0400000000000000003n); // → SyntaxError
Gardez à l'esprit que vous ne pouvez pas utiliser l'opérateur d'égalité stricte pour comparer un BigInt
à un nombre normal, car ils ne sont pas du même type :
console.log(10n === 10); // → false console.log(typeof 10n); // → bigint console.log(typeof 10); // → number
Au lieu de cela, vous pouvez utiliser l'opérateur d'égalité, qui effectue une conversion de type implicite avant de comparer ses opérandes :
console.log(10n == 10); // → true
Tous les opérateurs arithmétiques peuvent être utilisés sur BigInt
s à l'exception de l'opérateur unaire plus ( +
) :
10n + 20n; // → 30n 10n - 20n; // → -10n +10n; // → TypeError: Cannot convert a BigInt value to a number -10n; // → -10n 10n * 20n; // → 200n 20n / 10n; // → 2n 23n % 10n; // → 3n 10n ** 3n; // → 1000n let x = 10n; ++x; // → 11n --x; // → 10n
La raison pour laquelle l'opérateur unaire plus ( +
) n'est pas pris en charge est que certains programmes peuvent s'appuyer sur l'invariant que +
produit toujours un Number
ou lève une exception. Changer le comportement de +
casserait également le code asm.js.
Naturellement, lorsqu'ils sont utilisés avec des opérandes BigInt
, les opérateurs arithmétiques sont censés renvoyer une valeur BigInt
. Par conséquent, le résultat de l'opérateur de division ( /
) est automatiquement tronqué. Par exemple:
25 / 10; // → 2.5 25n / 10n; // → 2n
Conversion de type implicite
Étant donné que la conversion de type implicite peut perdre des informations, les opérations mixtes entre BigInt
s et Number
s ne sont pas autorisées. Lors du mélange de grands entiers et de nombres à virgule flottante, la valeur résultante peut ne pas être représentable avec précision par BigInt
ou Number
. Considérez l'exemple suivant :
(9007199254740992n + 1n) + 0.5
Le résultat de cette expression est en dehors du domaine de BigInt
et Number
. Un Number
avec une partie fractionnaire ne peut pas être converti avec précision en BigInt
. Et un BigInt
supérieur à 2 53 ne peut pas être converti avec précision en Number
.
En raison de cette restriction, il n'est pas possible d'effectuer des opérations arithmétiques avec un mélange d'opérandes Number
et BigInt
. Vous ne pouvez pas non plus transmettre un BigInt
aux API Web et aux fonctions JavaScript intégrées qui attendent un Number
. Tenter de le faire provoquera une TypeError
:
10 + 10n; // → TypeError Math.max(2n, 4n, 6n); // → TypeError
Notez que les opérateurs relationnels ne suivent pas cette règle, comme illustré dans cet exemple :
10n > 5; // → true
Si vous souhaitez effectuer des calculs arithmétiques avec BigInt
et Number
, vous devez d'abord déterminer le domaine dans lequel l'opération doit être effectuée. Pour ce faire, convertissez simplement l'un des opérandes en appelant Number()
ou BigInt()
:
BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20
Lorsqu'il est rencontré dans un contexte Boolean
, BigInt
est traité de la même manière que Number
. En d'autres termes, un BigInt
est considéré comme une valeur de vérité tant qu'il n'est pas 0n
:
if (5n) { // this code block will be executed } if (0n) { // but this code block won't }
Aucune conversion de type implicite entre les types BigInt
et Number
ne se produit lors du tri d'un tableau :
const arr = [3n, 4, 2, 1n, 0, -1n]; arr.sort(); // → [-1n, 0, 1n, 2, 3n, 4]
Opérateurs au niveau du bit tels que |
, &
, <<
, >>
et ^
fonctionnent sur BigInt
s de la même manière que Number
s. Les nombres négatifs sont interprétés comme des compléments à deux de longueur infinie. Les opérandes mixtes ne sont pas autorisés. Voici quelques exemples:
90 | 115; // → 123 90n | 115n; // → 123n 90n | 115; // → TypeError
Le constructeur BigInt
Comme avec les autres types primitifs, un BigInt
peut être créé à l'aide d'une fonction constructeur. L'argument passé à BigInt()
est automatiquement converti en BigInt
, si possible :
BigInt("10"); // → 10n BigInt(10); // → 10n BigInt(true); // → 1n
Les types de données et les valeurs qui ne peuvent pas être converties génèrent une exception :
BigInt(10.2); // → RangeError BigInt(null); // → TypeError BigInt("abc"); // → SyntaxError
Vous pouvez effectuer directement des opérations arithmétiques sur un BigInt
créé à l'aide d'un constructeur :
BigInt(10) * 10n; // → 100n
Lorsqu'ils sont utilisés comme opérandes de l'opérateur d'égalité stricte, les BigInt
créés à l'aide d'un constructeur sont traités de la même manière que les normaux :
BigInt(true) === 1n; // → true
Fonctions de la bibliothèque
JavaScript fournit deux fonctions de bibliothèque pour représenter les valeurs BigInt
sous forme d'entiers signés ou non signés :
-
BigInt.asUintN(width, BigInt)
: enveloppe unBigInt
entre 0 et 2 width -1 -
BigInt.asIntN(width, BigInt)
: enveloppe unBigInt
entre -2 width-1 et 2 width-1 -1
Ces fonctions sont particulièrement utiles lors de l'exécution d'opérations arithmétiques 64 bits. De cette façon, vous pouvez rester dans la plage prévue.
Prise en charge du navigateur et transpilation
Au moment d'écrire ces lignes, Chrome +67 et Opera +54 prennent entièrement en charge le type de données BigInt
. Malheureusement, Edge et Safari ne l'ont pas encore implémenté. Firefox ne prend pas en charge BigInt
par défaut, mais il peut être activé en définissant javascript.options.bigint
sur true
dans about:config
. Une liste à jour des navigateurs pris en charge est disponible sur Puis-je utiliser….
Malheureusement, transpiler BigInt
est un processus extrêmement compliqué, qui entraîne de lourdes pénalités de performance à l'exécution. Il est également impossible de remplir directement BigInt
car la proposition modifie le comportement de plusieurs opérateurs existants. Pour l'instant, une meilleure alternative consiste à utiliser la bibliothèque JSBI, qui est une implémentation en pur JavaScript de la proposition BigInt
.
Cette bibliothèque fournit une API qui se comporte exactement de la même manière que le BigInt
natif. Voici comment vous pouvez utiliser JSBI :
import JSBI from './jsbi.mjs'; const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER); const b2 = JSBI.BigInt('10'); const result = JSBI.add(b1, b2); console.log(String(result)); // → '9007199254741001'
L'un des avantages de l'utilisation de JSBI est qu'une fois la prise en charge du navigateur améliorée, vous n'aurez plus besoin de réécrire votre code. Au lieu de cela, vous pouvez automatiquement compiler votre code JSBI en code BigInt
natif en utilisant un plugin babel. De plus, les performances de JSBI sont comparables à celles des implémentations BigInt
natives. Vous pouvez vous attendre à une prise en charge plus large des navigateurs pour BigInt
bientôt.
Conclusion
BigInt
est un nouveau type de données destiné à être utilisé lorsque les valeurs entières sont supérieures à la plage prise en charge par le type de données Number
. Ce type de données nous permet d'effectuer en toute sécurité des opérations arithmétiques sur de grands nombres entiers, de représenter des horodatages haute résolution, d'utiliser de grands ID entiers, etc., sans avoir besoin d'utiliser une bibliothèque.
Il est important de garder à l'esprit que vous ne pouvez pas effectuer d'opérations arithmétiques avec un mélange d'opérandes Number
et BigInt
. Vous devrez déterminer le domaine dans lequel l'opération doit être effectuée en convertissant explicitement l'un ou l'autre des opérandes. De plus, pour des raisons de compatibilité, vous n'êtes pas autorisé à utiliser l'opérateur unaire plus ( +
) sur un BigInt
.
Qu'est-ce que tu penses? Trouvez-vous BigInt
utile ? Faites le nous savoir dans les commentaires!