Un guide complet sur le type BigInt de JavaScript, couvrant ses fonctionnalités, son utilisation et ses applications pour la gestion de l'arithmétique des grands entiers. Apprenez à surmonter les limites de JavaScript et à effectuer des calculs complexes avec précision.
JavaScript BigInt : Maîtriser l'arithmétique des grands entiers
JavaScript, bien qu'étant un langage polyvalent, a des limites lorsqu'il s'agit de traiter de très grands nombres entiers. Le type standard `Number` ne peut représenter avec précision les entiers que jusqu'à une certaine limite, connue sous le nom de `Number.MAX_SAFE_INTEGER`. Au-delà de cette limite, les calculs deviennent imprécis, entraînant des résultats inattendus. C'est là que BigInt
vient à la rescousse. Introduit dans ECMAScript 2020, BigInt
est un objet intégré qui offre un moyen de représenter et de manipuler des entiers de taille arbitraire, dépassant les limitations du type `Number` standard.
Comprendre le besoin de BigInt
Avant BigInt
, les développeurs JavaScript devaient s'appuyer sur des bibliothèques ou des implémentations personnalisées pour gérer les calculs avec de grands entiers. Ces solutions entraînaient souvent une surcharge de performance et une complexité accrue. L'introduction de BigInt
a fourni un moyen natif et efficace de travailler avec de grands entiers, ouvrant des possibilités pour des applications dans divers domaines, notamment :
- Cryptographie : La gestion sécurisée de grands nombres premiers est cruciale pour les algorithmes cryptographiques.
- Calculs financiers : Représenter avec précision de grandes valeurs monétaires sans perte de précision.
- Calcul scientifique : Effectuer des calculs complexes impliquant des nombres extrêmement grands ou petits.
- Horodatages de haute précision : Représenter des horodatages avec une précision à la nanoseconde.
- Génération d'ID : Créer des identifiants uniques et très grands.
Créer des valeurs BigInt
Il existe deux manières principales de créer des valeurs BigInt
en JavaScript :
- Utiliser le constructeur `BigInt()` : Ce constructeur peut convertir une valeur de type nombre, chaîne de caractères ou booléen en un
BigInt
. - Utiliser le suffixe `n` : Ajouter `n` à un littéral entier crée un
BigInt
.
Exemples :
Utilisation du constructeur `BigInt()` :
const bigIntFromNumber = BigInt(12345678901234567890);
const bigIntFromString = BigInt("98765432109876543210");
const bigIntFromBoolean = BigInt(true); // Résultat : 1n
const bigIntFromFalseBoolean = BigInt(false); // Résultat : 0n
console.log(bigIntFromNumber); // Sortie : 12345678901234567890n
console.log(bigIntFromString); // Sortie : 98765432109876543210n
console.log(bigIntFromBoolean); // Sortie : 1n
console.log(bigIntFromFalseBoolean); // Sortie : 0n
Utilisation du suffixe `n` :
const bigIntLiteral = 12345678901234567890n;
console.log(bigIntLiteral); // Sortie : 12345678901234567890n
Remarque importante : Vous ne pouvez pas mélanger directement les valeurs BigInt
et Number
dans les opérations arithmétiques. Vous devez les convertir explicitement au même type avant d'effectuer des calculs. Tenter de les mélanger directement entraînera une `TypeError`.
Opérations arithmétiques avec BigInt
BigInt
prend en charge la plupart des opérateurs arithmétiques standard, notamment :
- Addition (`+`)
- Soustraction (`-`)
- Multiplication (`*`)
- Division (`/`)
- Reste (`%`)
- Exponentiation (`**`)
Exemples :
const a = 12345678901234567890n;
const b = 98765432109876543210n;
const sum = a + b;
const difference = a - b;
const product = a * b;
const quotient = a / 2n; // Remarque : La division tronque vers zéro
const remainder = a % 7n;
const power = a ** 3n; // L'exponentiation fonctionne comme prévu
console.log("Somme :", sum); // Sortie : Somme : 111111111011111111100n
console.log("Différence :", difference); // Sortie : Différence : -86419753208641975320n
console.log("Produit :", product); // Sortie : Produit : 1219326311370217957951669538098765432100n
console.log("Quotient :", quotient); // Sortie : Quotient : 6172839450617283945n
console.log("Reste :", remainder); // Sortie : Reste : 5n
console.log("Puissance :", power); // Sortie : Puissance : 187641281029182300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n
Considérations importantes :
- Division : La division avec les valeurs
BigInt
tronque vers zéro. Cela signifie que la partie décimale du résultat est ignorée. Si vous avez besoin d'une division plus précise, envisagez d'utiliser des bibliothèques qui prennent en charge l'arithmétique à précision arbitraire. - Opérateur unaire plus (+) : L'opérateur unaire plus (+) ne peut pas être utilisé avec les valeurs
BigInt
car cela entrerait en conflit avec l'ancien code asm.js. Utilisez la fonction de conversion `Number()` pour convertir un BigInt en Number si vous avez besoin d'une représentation numérique (en sachant que vous pourriez perdre en précision). - Opérateurs binaires (bitwise) :
BigInt
prend également en charge les opérateurs binaires tels que `&`, `|`, `^`, `~`, `<<` et `>>`. Ces opérateurs fonctionnent comme prévu sur la représentation binaire des valeursBigInt
.
Opérateurs de comparaison
Vous pouvez utiliser les opérateurs de comparaison standard (`==`, `!=`, `<`, `>`, `<=`, `>=`) pour comparer des valeurs BigInt
avec d'autres valeurs BigInt
ou même avec des valeurs Number
. Cependant, soyez attentif au potentiel de coercition de type.
Exemples :
const a = 10n;
const b = 20n;
const c = 10;
console.log(a == b); // Sortie : false
console.log(a != b); // Sortie : true
console.log(a < b); // Sortie : true
console.log(a > b); // Sortie : false
console.log(a <= b); // Sortie : true
console.log(a >= b); // Sortie : false
console.log(a == c); // Sortie : true (coercition de type)
console.log(a === c); // Sortie : false (pas de coercition de type)
Meilleure pratique : Utilisez l'égalité stricte (`===`) et l'inégalité stricte (`!==`) pour éviter la coercition de type inattendue lors de la comparaison des valeurs BigInt
et Number
.
Conversion entre BigInt et Number
Bien que les opérations arithmétiques directes entre BigInt
et Number
ne soient pas autorisées, vous pouvez convertir entre les deux types. Cependant, soyez conscient de la perte potentielle de précision lors de la conversion d'un BigInt
en Number
si la valeur du BigInt
dépasse `Number.MAX_SAFE_INTEGER`.
Exemples :
const bigIntValue = 9007199254740991n; // Number.MAX_SAFE_INTEGER
const numberValue = Number(bigIntValue); // Conversion de BigInt en Number
console.log(numberValue); // Sortie : 9007199254740991
const largerBigIntValue = 9007199254740992n; // Dépasse Number.MAX_SAFE_INTEGER
const largerNumberValue = Number(largerBigIntValue);
console.log(largerNumberValue); // Sortie : 9007199254740992 (peut être imprécis)
const numberToBigInt = BigInt(12345); // Conversion de Number en BigInt
console.log(numberToBigInt); // Sortie : 12345n
Cas d'utilisation et exemples
Cryptographie
Les algorithmes cryptographiques reposent souvent sur de très grands nombres premiers pour la sécurité. BigInt
offre un moyen de représenter et de manipuler ces nombres efficacement.
// Exemple : Génération d'une paire de clés simple (non sécurisée)
function generateKeyPair() {
const p = 281n; // Un nombre premier
const q = 283n; // Un autre nombre premier
const n = p * q; // Modulo
const totient = (p - 1n) * (q - 1n); // Fonction indicatrice d'Euler
// Choisir un e (exposant public) tel que 1 < e < totient et pgcd(e, totient) = 1
const e = 17n;
// Calculer d (exposant privé) tel que (d * e) % totient = 1
let d = 0n;
for (let i = 1n; i < totient; i++) {
if ((i * e) % totient === 1n) {
d = i;
break;
}
}
return {
publicKey: { n, e },
privateKey: { n, d },
};
}
const keyPair = generateKeyPair();
console.log("Clé publique :", keyPair.publicKey);
console.log("Clé privée :", keyPair.privateKey);
Note : Ceci est un exemple simplifié à des fins de démonstration uniquement. La cryptographie du monde réel utilise des nombres premiers beaucoup plus grands et des algorithmes plus sophistiqués.
Calculs financiers
Lorsqu'on traite de grosses sommes d'argent, en particulier dans les transactions internationales, la précision est essentielle. BigInt
peut prévenir les erreurs d'arrondi et garantir des calculs précis.
// Exemple : Calcul des intérêts composés
function calculateCompoundInterest(principal, rate, time) {
const principalBigInt = BigInt(principal * 100); // Convertir en centimes
const rateBigInt = BigInt(rate * 10000); // Convertir en dix-millièmes de pourcent
const timeBigInt = BigInt(time);
let amount = principalBigInt;
for (let i = 0n; i < timeBigInt; i++) {
amount = amount * (10000n + rateBigInt) / 10000n;
}
const amountInDollars = Number(amount) / 100;
return amountInDollars;
}
const principal = 1000000; // 1 000 000 $
const rate = 0.05; // Taux d'intérêt de 5%
const time = 10; // 10 ans
const finalAmount = calculateCompoundInterest(principal, rate, time);
console.log("Montant final :", finalAmount); // Sortie : Montant final : 1628894.6267774413 (approximativement)
Dans cet exemple, nous convertissons le principal et le taux en valeurs BigInt
pour éviter les erreurs d'arrondi pendant le calcul. Le résultat est ensuite reconverti en Number
pour l'affichage.
Travailler avec de grands ID
Dans les systèmes distribués, la génération d'ID uniques sur plusieurs serveurs peut être un défi. L'utilisation de BigInt
vous permet de créer de très grands ID qui ont peu de chances d'entrer en collision.
// Exemple : Génération d'un ID unique basé sur l'horodatage et l'ID du serveur
function generateUniqueId(serverId) {
const timestamp = BigInt(Date.now());
const serverIdBigInt = BigInt(serverId);
const random = BigInt(Math.floor(Math.random() * 1000)); // Ajouter un peu d'aléa
// Combiner les valeurs pour créer un ID unique
const uniqueId = (timestamp << 20n) + (serverIdBigInt << 10n) + random;
return uniqueId.toString(); // Retourner sous forme de chaîne pour une manipulation facile
}
const serverId = 123; // ID de serveur d'exemple
const id1 = generateUniqueId(serverId);
const id2 = generateUniqueId(serverId);
console.log("ID Unique 1 :", id1);
console.log("ID Unique 2 :", id2);
BigInt et JSON
Les valeurs BigInt
ne sont pas prises en charge nativement par JSON. Tenter de sérialiser un objet JavaScript contenant un BigInt
avec `JSON.stringify()` entraînera une `TypeError`. Pour gérer les valeurs BigInt
lorsque vous travaillez avec JSON, vous avez plusieurs options :
- Convertir en chaîne de caractères : Convertissez le
BigInt
en chaîne de caractères avant la sérialisation. C'est l'approche la plus courante et la plus simple. - Sérialisation/Désérialisation personnalisée : Utilisez une fonction de sérialisation/désérialisation personnalisée pour gérer les valeurs
BigInt
.
Exemples :
Conversion en chaîne de caractères :
const data = {
id: 12345678901234567890n,
name: "Example Data",
};
// Convertir le BigInt en chaîne de caractères avant la sérialisation
data.id = data.id.toString();
const jsonData = JSON.stringify(data);
console.log(jsonData); // Sortie : {"id":"12345678901234567890","name":"Example Data"}
// Lors de la désérialisation, vous devrez reconvertir la chaîne en BigInt
const parsedData = JSON.parse(jsonData, (key, value) => {
if (key === "id") {
return BigInt(value);
}
return value;
});
console.log(parsedData.id); // Sortie : 12345678901234567890n
Sérialisation/Désérialisation personnalisée (avec `replacer` et `reviver`) :
const data = {
id: 12345678901234567890n,
name: "Example Data",
};
// Sérialisation personnalisée
const jsonData = JSON.stringify(data, (key, value) => {
if (typeof value === 'bigint') {
return value.toString();
} else {
return value;
}
});
console.log(jsonData);
// Désérialisation personnalisée
const parsedData = JSON.parse(jsonData, (key, value) => {
if (typeof value === 'string' && /^[0-9]+$/.test(value)) { //vérifier si c'est un nombre et une chaîne de caractères
try {
return BigInt(value);
} catch(e) {
return value;
}
}
return value;
});
console.log(parsedData.id);
Compatibilité des navigateurs
BigInt
est largement pris en charge dans les navigateurs modernes. Cependant, il est essentiel de vérifier la compatibilité avec les navigateurs ou environnements plus anciens. Vous pouvez utiliser un outil comme Can I use pour vérifier la prise en charge par les navigateurs. Si vous devez prendre en charge des navigateurs plus anciens, vous pourriez envisager d'utiliser un polyfill, mais sachez que les polyfills peuvent avoir un impact sur les performances.
Considérations sur les performances
Bien que BigInt
offre un moyen puissant de travailler avec de grands entiers, il est important d'être conscient des implications potentielles sur les performances.
- Les opérations
BigInt
peuvent être plus lentes que les opérationsNumber
standard. - La conversion entre
BigInt
etNumber
peut également introduire une surcharge.
Par conséquent, n'utilisez BigInt
que lorsque c'est nécessaire, et optimisez votre code pour les performances si vous effectuez un grand nombre d'opérations BigInt
.
Conclusion
BigInt
est un ajout précieux à JavaScript, permettant aux développeurs de gérer l'arithmétique des grands entiers avec précision. En comprenant ses fonctionnalités, ses limites et ses cas d'utilisation, vous pouvez tirer parti de BigInt
pour créer des applications robustes et précises dans divers domaines, notamment la cryptographie, les calculs financiers et le calcul scientifique. N'oubliez pas de prendre en compte la compatibilité des navigateurs et les implications sur les performances lorsque vous utilisez BigInt
dans vos projets.
Pour aller plus loin
- Mozilla Developer Network (MDN) - BigInt
- Blog V8 - BigInt : Entiers à précision arbitraire en JavaScript
Ce guide offre un aperçu complet de BigInt
en JavaScript. Explorez les ressources liées pour des informations plus approfondies et des techniques avancées.