Menu
Français

Nombres et BigInt en JavaScript : précision et grands entiers

Comprendre le type Number de JavaScript : virgule flottante, MAX_SAFE_INTEGER, et quand passer à BigInt pour gérer les très grands entiers.

Un seul type pour (presque) tous les nombres

La plupart des langages distinguent les entiers des flottants. JavaScript, lui, a longtemps fait le choix inverse : un seul et unique type, Number. Que tu écrives 42, 3.14 ou -0.001, tu obtiens toujours la même primitive — un flottant double précision sur 64 bits au format IEEE 754.

index.js
Output
Click Run to see the output here.

C'est pratique — pas besoin de convertir entre int et float, pas de débordement à 2^31. Mais cette représentation en flottant a un prix, et les débutants s'y cassent les dents en permanence. Un second type numérique, BigInt, est arrivé en 2020 pour combler les limites du type Number.

Le piège des flottants

Essaie ce code :

index.js
Output
Click Run to see the output here.

La première ligne affiche 0.30000000000000004. La seconde affiche false. Ce n'est pas une bizarrerie propre à JavaScript — Python, Java, C et n'importe quel langage qui s'appuie sur les flottants IEEE 754 se comportent exactement pareil.

La raison ? 0.1 et 0.2 n'ont pas de représentation exacte en binaire, un peu comme 1/3 qu'on ne peut pas écrire exactement en décimal. Le moteur stocke l'approximation binaire la plus proche, et les petites erreurs finissent par s'accumuler. Le bon modèle mental, c'est de considérer les valeurs Number contenant des décimales comme des approximations très proches de ce que vous avez tapé, mais jamais identiques.

Pour de l'argent, ne stockez pas 19,99 € sous la forme 19.99. Stockez plutôt les centimes comme des entiers — 1999 — et formatez à l'affichage. C'est de loin la meilleure habitude à prendre pour éviter les bugs liés aux flottants.

Comparer des flottants sans se faire avoir

Puisqu'on ne peut pas faire confiance à l'égalité stricte, comparez avec une tolérance quand vous en avez besoin :

index.js
Output
Click Run to see the output here.

Number.EPSILON représente la plus petite différence entre 1 et le nombre représentable juste après — une tolérance raisonnable par défaut pour des valeurs proches de 1. Pour des ordres de grandeur très grands ou très petits, mieux vaut utiliser une tolérance qui s'adapte aux valeurs manipulées.

La plage des entiers sûrs en JavaScript

Jusqu'à une certaine taille, les entiers sont représentés de manière exacte dans un flottant 64 bits. Au-delà, on perd de la précision bit après bit :

index.js
Output
Click Run to see the output here.

2^53 - 1, c'est le dernier entier en dessous duquel chaque entier reste représentable exactement. Au-delà, certains entiers n'existent tout simplement pas dans le type Number : ils sont arrondis au voisin le plus proche. C'est typiquement le genre de corruption silencieuse des données qui te tombe dessus si tu parses des identifiants 64 bits venus d'une base comme de simples nombres JSON.

BigInt entre en scène

BigInt est une primitive à part, dédiée aux entiers de précision arbitraire. Pour en créer un, tu ajoutes n à la fin d'un littéral entier, ou tu appelles BigInt(...) :

index.js
Output
Click Run to see the output here.

Les BigInt n'ont pas de limite haute, hormis la mémoire disponible. C'est l'outil idéal dans ces cas-là :

  • Les identifiants de base de données ou les snowflake IDs de Twitter/X qui dépassent 2^53.
  • Les calculs cryptographiques.
  • Toute arithmétique sur des entiers où la justesse du résultat prime sur la vitesse brute.

En revanche, ce n'est pas pertinent pour les compteurs du quotidien, les indices de tableaux ou les montants en centimes : le type Number classique est plus rapide et s'intègre avec toutes les API du langage.

Opérations arithmétiques sur les BigInt

Tous les opérateurs habituels fonctionnent, à condition que les deux opérandes soient des BigInt :

index.js
Output
Click Run to see the output here.

La division tronque vers zéro — les BigInt fractionnaires, ça n'existe pas. Si tu as besoin d'une fraction, il faudra repasser sur du Number (ou utiliser une bibliothèque décimale dédiée).

Ne mélange pas les types

Le piège classique : impossible de mélanger Number et BigInt dans une même expression.

index.js
Output
Click Run to see the output here.

La comparaison fait figure d'exception : <, > et == acceptent de franchir la frontière entre les deux types :

index.js
Output
Click Run to see the output here.

Donc == les considère comme égaux, mais pas ===. Si tu utilises déjà === partout (et tu devrais), vois les comparaisons numériques entre les deux types comme un signal d'alerte : choisis un camp et fais la conversion.

Convertir entre number et bigint

Deux conversions, deux pièges à éviter :

index.js
Output
Click Run to see the output here.

Le passage de Number vers BigInt est strict : les décimales et NaN déclenchent une erreur. À l'inverse, de BigInt vers Number, c'est permissif mais on perd en précision — tout ce qui dépasse MAX_SAFE_INTEGER est arrondi. Si tu convertis un BigInt qui vient d'un serveur, demande-toi d'abord si tu en as vraiment besoin.

Les valeurs spéciales du type Number

Tant qu'on y est, il existe trois valeurs du type Number qui ne sont pas des nombres au sens mathématique :

index.js
Output
Click Run to see the output here.

Infinity et -Infinity apparaissent lors d'une division par zéro ou quand on dépasse la plage des flottants. Quant à NaN (« not a number »), il pointe le bout de son nez quand une opération arithmétique ne produit aucun résultat exploitable.

Petite particularité célèbre : NaN n'est pas égal à lui-même. Ce n'est pas un bug de JS, c'est écrit noir sur blanc dans la spec IEEE 754. Pour le détecter, utilise Number.isNaN(x). L'ancienne fonction globale isNaN, elle, convertit d'abord son argument, ce qui donne des résultats faux (isNaN("hello") renvoie true). Privilégie toujours Number.isNaN.

Convertir une chaîne en nombre en JavaScript

Les saisies utilisateur et les nombres issus de JSON arrivent souvent sous forme de chaînes. Voici trois façons de les convertir :

index.js
Output
Click Run to see the output here.

Number() est strict : toute valeur non numérique renvoie NaN, à l'exception de la chaîne vide et des espaces, qui donnent 0. À l'inverse, parseInt et parseFloat sont tolérants : ils lisent tant qu'ils peuvent puis s'arrêtent. Choisissez celui qui correspond à votre intention, et vérifiez toujours NaN avant d'utiliser le résultat.

Pour parser un BigInt depuis une chaîne, utilisez BigInt("123") : c'est strict et ça lève une exception en cas d'entrée invalide.

Un petit aide-mémoire

  • Pour les compteurs, les calculs, les coordonnées et la plupart des nombres du quotidien : utilisez Number.
  • Pour l'argent : convertissez en centimes entiers et utilisez Number, ou passez par une bibliothèque de décimaux.
  • Pour les entiers plus grands que 2^53 (IDs de base de données, crypto, combinatoire) : utilisez BigInt avec le suffixe n.
  • Comparez les flottants avec une marge de tolérance, jamais avec ===.
  • Détectez les résultats invalides avec Number.isNaN et Number.isFinite, pas avec les versions globales.
  • Ne mélangez pas Number et BigInt dans une même expression : convertissez explicitement.

La suite : null vs undefined

JavaScript propose deux façons de dire « pas de valeur » — null et undefined — et elles ne sont pas interchangeables. Au programme : ce que chacune signifie, en quoi elles diffèrent, et laquelle choisir selon le contexte.

Questions fréquentes

Pourquoi 0.1 + 0.2 ne donne-t-il pas 0.3 en JavaScript ?

Parce que le type Number de JavaScript est un flottant 64 bits au format IEEE 754, et que 0.1 et 0.2 n'ont pas de représentation binaire exacte. Le résultat renvoyé est 0.30000000000000004. Ce n'est pas un bug de JavaScript : le même phénomène se produit en Python, en Java et dans tout langage qui utilise ce format de flottant. Pour manipuler de l'argent, travaillez en entiers (centimes) ou utilisez une bibliothèque décimale dédiée.

Qu'est-ce que BigInt en JavaScript et quand l'utiliser ?

BigInt est une primitive numérique distincte, conçue pour les entiers qui dépassent Number.MAX_SAFE_INTEGER (2^53 - 1). On en crée un en ajoutant un n à la fin — 9007199254740993n — ou via BigInt(value). C'est l'outil à sortir pour les identifiants de base de données 64 bits, la cryptographie, ou tout calcul sur entiers où la précision prime sur la performance.

Peut-on mélanger Number et BigInt en JavaScript ?

Non. 1n + 1 lève une erreur TypeError: Cannot mix BigInt and other types. Il faut convertir explicitement avec BigInt(n) ou Number(b). Les opérateurs de comparaison comme < et == fonctionnent entre les deux types, mais === renvoie toujours false puisque les types diffèrent.

Apprendre à coder avec Coddy

COMMENCER