JSON Toolkit

Pourquoi les grands entiers perdent de la précision

L’essentiel : Les entiers au-delà de 2^53-1 perdent de la précision dans JSON.parse. Les identifiants snowflake, numéros de commande et bigints de base de données y sont tous exposés, donc gardez-les en chaînes de bout en bout.

Intermédiaire~4 min de lecturePublié le 2026-10-30Mis à jour le 2026-11-04
Pourquoi les grands entiers perdent de la précision en JSON et comment les préserver

L'essentiel : traitez les identifiants snowflake, numéros de commande et clés primaires bigint comme des chaînes tout au long de la sérialisation, du transport et du parsing ; ne gardez le type numérique que lorsque le champ est prouvé rester dans 2^53-1, et imposez-le alors avec une contrainte maximum dans le Schema.

Parce que les dégâts se produisent pendant le parsing, silencieusement.

JSON.parse('{"id": 9007199254740993}');
// { id: 9007199254740992 }

Le chiffre manquant a disparu ; il n'y a rien à récupérer.

D'où vient 2^53

JavaScript n'a qu'un type numérique : le double précision IEEE 754, avec 53 bits de mantisse (un implicite, 52 explicites). Les entiers exacts s'arrêtent donc à ±(2^53-1) :

-9007199254740991 .. 9007199254740991

Au-delà de cette ligne, les valeurs représentables consécutives sont espacées de 2, 4, 8. 9007199254740993 est impair, et à cette magnitude chaque valeur représentable est paire, donc il s'arrondit à 9007199254740992.

Les champs touchés

Champ Chiffres Risque
Identifiants snowflake 18 à 19 Élevé, les derniers chiffres changent
Numéros de commande / transaction 16 à 20 Élevé
Clés primaires bigint de base de données 19 Élevé
Argent en unités mineures — Faible
Horodatages en millisecondes 13 Sûr

Le fil conducteur : ce sont des identifiants, pas des quantités. Un identifiant est transporté, jamais additionné ni multiplié, donc la chaîne est le bon type.

Quatre corrections

Sérialisez en chaîne. Le backend convertit les champs bigint avant d'écrire la réponse. Simple et durable ; documentez que ces champs sont des chaînes.

Laissez le sérialiseur s'en charger. Le ToStringSerializer de Jackson pour Long, le tag json:",string" de Go. Même idée : garder la valeur hors du chemin numérique.

Parsez avec le support BigInt. json-bigint et bibliothèques similaires. Notez que BigInt ne peut pas se mélanger à l'arithmétique Number, et que JSON.stringify le rejette.

Contraignez la plage. Si le champ ne peut réellement pas dépasser 2^53, ajoutez maximum dans le Schema pour que les données hors plage échouent à la validation au lieu d'échouer silencieusement dans le navigateur.

Comment savoir si vous avez ce bug

Prenez un identifiant connu et faites-lui un aller-retour : sérialisez, transmettez, parsez, comparez caractère par caractère. Ou inspectez le corps de réponse brut — un nombre non cité de 19 chiffres qui arrive avec les mauvais derniers chiffres est presque certainement une perte au parsing.

FAQ

Pourquoi JSON.parse change-t-il les derniers chiffres sans erreur ?

JavaScript n'a qu'un type numérique, le double précision IEEE 754, donc les entiers au-delà de 2^53-1 ne peuvent atterrir que sur la valeur représentable la plus proche. JSON.parse traite cela comme une conversion numérique normale : ni exception ni avertissement.

Comment le frontend peut-il garder les grands entiers intacts ?

Utilisez une bibliothèque comme json-bigint, car JSON.parse ne peut pas le faire. Le coût est que BigInt ne peut pas se mélanger à l'arithmétique Number et que JSON.stringify le rejette, donc le code environnant doit aussi changer.

La doc d'API doit-elle préciser que ces champs sont des chaînes ?

Oui. Sérialiser en chaînes est la correction la plus simple et la plus durable, mais elle ne fonctionne que si les consommateurs traitent la valeur comme du texte, donc le type de champ doit être écrit dans le contrat d'API.

Lectures complémentaires