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.
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 :
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 :
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 :
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(...) :
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 :
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.
La comparaison fait figure d'exception : <, > et == acceptent de franchir la frontière entre les deux types :
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 :
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 :
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 :
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) : utilisezBigIntavec le suffixen. - Comparez les flottants avec une marge de tolérance, jamais avec
===. - Détectez les résultats invalides avec
Number.isNaNetNumber.isFinite, pas avec les versions globales. - Ne mélangez pas
NumberetBigIntdans 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.