Quelle est la façon acceptée d'envoyer des valeurs 64 bits sur JSON?
Certaines de mes données sont des entiers 64 bits. Je voudrais les envoyer à un programme JavaScript s'exécutant sur une page.
Cependant, pour autant que je sache, les entiers dans la plupart des implémentations JavaScript sont des quantités signées 32 bits.
Mes deux options semblent être:
- envoie les valeurs sous forme de chaînes
- envoie les valeurs sous forme de nombres à virgule flottante 64 bits
Option (1) n'est pas parfait, mais l'option (2) semble beaucoup moins parfaite (perte de données).
Comment avez-vous géré cette situation?
5 réponses
Cela semble être moins un problème avec JSON et plus un problème avec Javascript lui-même. Que comptez-vous faire de ces chiffres? Si c'est juste un jeton magique que vous devez transmettre au site Web plus tard, utilisez simplement une chaîne contenant la valeur. Si vous devez réellement faire de l'arithmétique sur la valeur, vous pouvez éventuellement écrire vos propres routines Javascript pour l'arithmétique 64 bits.
Une façon dont vous pourriez représenter des valeurs en Javascript (et donc JSON) serait en divisant les nombres en deux valeurs 32 bits, par exemple.
[ 12345678, 12345678 ]
Pour diviser une valeur 64 bits en deux valeurs 32 bits, faites quelque chose comme ceci:
output_values[0] = (input_value >> 32) & 0xffffffff;
output_values[1] = input_value & 0xffffffff;
Puis pour recombiner deux valeurs de 32 bits à une valeur de 64 bits:
input_value = ((int64_t) output_values[0]) << 32) | output_values[1];
Le type de nombre de Javascript (64 bits IEEE 754) n'a qu'environ 53 bits de précision.
Mais, si vous n'avez pas besoin de faire d'addition ou de multiplication, vous pouvez conserver la valeur 64 bits sous forme de chaînes à 4 caractères car JavaScript utilise UTF-16.
Par exemple, 1 peut être codé comme "\u0000\u0000\u0000\u0001". Cela a l'avantage que la comparaison de valeur ( = = ,>,
function and64(a,b) {
var r = "";
for (var i = 0; i < 4; i++)
r += String.fromCharCode(a.charCodeAt(i) & b.charCodeAt(i));
return r;
}
Il y a en fait une limitation au niveau de précision JavaScript/ECMAScript à 53 bits pour les entiers (ils sont stockés dans la mantisse d'un tampon de mémoire "double" de 8 octets). Ainsi, la transmission de grands nombres en tant que JSON ne sera pas non sérialisée comme prévu par le client JavaScript, ce qui les tronquerait à sa résolution de 53 bits.
> parseInt("10765432100123456789")
10765432100123458000
Voir le Number.MAX_SAFE_INTEGER
fonction constante et Number.isSafeInteger()
:
La constante
MAX_SAFE_INTEGER
a une valeur de9007199254740991
. Le raisonnement derrière cela le nombre est que JavaScript utilise la double précision nombres de format à virgule flottante comme spécifié dans IEEE 754 et ne peut représenter en toute sécurité les nombres entre-(2^53 - 1)
et2^53 - 1
.Safe dans ce contexte se réfère à la capacité de représenter des entiers exactement et de les comparer correctement. Exemple,
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2
volonté évaluer àtrue
, ce qui est mathématiquement incorrect. VoirNumber.isSafeInteger()
pour plus d'informations.
En raison de la résolution des flottants en JavaScript, en utilisant " 64-bit floating numéros de points " comme vous l'avez proposé souffrirait de la même restriction.
IMHO la meilleure option est de transmettre des valeurs telles que le texte. Ce serait toujours un contenu JSON parfaitement lisible, et il serait facile de travailler avec au niveau JavaScript.
Une représentation "pure string" est ce que OData spécifie, pour ses types Edm.Int64
ou Edm.Decimal
.
Ce que L'API Twitter fait dans ce cas, c'est d'ajouter un champ ".._str":
spécifique dans le JSON, en tant que tel:
{
"id": 10765432100123456789, // for JSON compliant clients
"id_str": "10765432100123456789", // for JavaScript
...
}
J'aime cette option beaucoup, car il serait toujours compatible avec les clients compatibles int64. En pratique, un tel contenu dupliqué dans le JSON ne fera pas beaucoup de mal, s'il est dégonflé/gzippé au niveau HTTP.
Une fois transmis sous forme de chaîne, vous pouvez utiliser des bibliothèques comme strint - une bibliothèque JavaScript pour les entiers codés par chaîne pour gérer de telles valeurs.
La représentation du nombre JS est un double IEEE standard, vous ne pouvez donc pas représenter un entier de 64 bits. iirc vous obtenez peut-être 48 bits de précision int réelle dans un double, mais tous les bitops js réduisent à 32 bits de précision (c'est ce que la spécification nécessite. yay!!!) donc, si vous avez vraiment besoin d'un int 64 bits dans js, vous devrez implémenter votre propre bibliothèque logique int 64 bits.
JSON lui-même ne se soucie pas des limites d'implémentation. votre problème est que JS ne peut pas gérer vos données, pas le protocole. En d'autres termes, votre code client JS doit utiliser l'une de ces options non parfaites.