发送64位值的JSON标准方法是什么?

46

我的一些数据是64位整数,我想将它们发送给运行在页面上的JavaScript程序。

然而,据我所知,大多数JavaScript实现中的整数是32位有符号量。

我的两个选择似乎是:

  1. 将值作为字符串发送
  2. 将值作为64位浮点数发送

选项(1)并非完美,但选项(2)似乎不太完美(会有数据丢失)。

您如何处理这种情况?


7
写代码的人们,如果遇到这个页面,就要倒霉了! - duhaime
6个回答

32

实际上,在JavaScript/ECMAScript级别上,整数的精度限制为53位(它们存储在“类似double”的8字节内存缓冲区的尾数中)。因此,将大数字作为JSON传输将无法按预期由JavaScript客户端反序列化,这会将它们截断到其53位分辨率。

> parseInt("10765432100123456789")
10765432100123458000

请查看 Number.MAX_SAFE_INTEGER 常量Number.isSafeInteger() 函数:

MAX_SAFE_INTEGER 常量的值为 9007199254740991。这个数字的背后原理是,JavaScript 使用 IEEE 754 规定的双精度浮点格式表示数字,只能安全地表示介于 -(2^53 - 1)2^53 - 1 之间的数字。

在此上下文中,“安全”指的是能够准确地表示整数并正确比较它们。例如,Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 的结果将会是 true,然而从数学角度来说是不正确的。有关更多信息,请参见 Number.isSafeInteger()

由于 JavaScript 中浮点数的分辨率限制,使用您提出的“64位浮点数”会遭受相同的限制。

我认为最好的选择是将这些值作为文本传输。这仍将是完全可读的 JSON 内容,并且很容易在 JavaScript 层面上处理。

OData 为其 Edm.Int64Edm.Decimal 类型指定了“纯字符串”表示方法

在此情况下,Twitter API 所做的是在 JSON 中添加一个特定的 ".._str": 字段,如下所示:

{
   "id": 10765432100123456789,           // for JSON compliant clients
   "id_str": "10765432100123456789",     // for JavaScript
    ...
}

我非常喜欢这个选项,因为它仍然与支持int64的客户端兼容。 实际上,在JSON中这样重复的内容不会对性能造成太大影响,如果在HTTP级别进行压缩,则效果更佳。

一旦以字符串形式传输,您可以使用类似 strint - 用于处理字符串编码整数的 JavaScript 库 的库来处理这些值。

更新:JavaScript 引擎的新版本包括一个BigInt对象类,它能够处理超过53位的整数。实际上,它可用于任意大的整数,因此非常适合64位整数值。但是,在将其序列化为JSON时,BigInt 值将作为 JSON 字符串序列化 - 这可能有些奇怪,但我想是出于兼容性考虑。


2
很好的解释。我刚遇到这个问题,使用JS前端和C# Web API后端。我的HttpResponseMessage会序列化我的对象(其中包含长ID),它们被截断了。真正奇怪的是,它们直到到达浏览器才被截断。如果我创建响应,然后在发送之前解析内容,ID就不会被截断。直到浏览器接收并解析响应后,它才会神奇地被截断。您的解释帮助我理解了原因。谢谢!最终我注册了一个自定义转换器来处理长类型。 - Kris Coleman

26
这似乎不是 JSON 的问题,而更多地是 Javascript 本身的问题。您打算如何处理这些数字?如果它只是一个您需要稍后传回网站的魔术令牌,请使用包含该值的字符串即可。如果您确实需要在该值上执行算术运算,您可以编写自己的 64 位算术 Javascript 程序。

在 Javascript(因此也是 JSON)中表示值的一种方法是将数字分成两个 32 位值,例如。
  [ 12345678, 12345678 ]

要将一个64位的值分成两个32位的值,可以像这样处理:

  output_values[0] = (input_value >> 32) & 0xffffffff;
  output_values[1] = input_value & 0xffffffff;

将两个32位的值重新组合成一个64位的值:

  input_value = ((int64_t) output_values[0]) << 32) | output_values[1];

1
当我尝试执行"input_value >> 32"时,我只会得到"input_value",好像这32位只是完全绕回来了一样。">>>"也有同样的效果。但是当我尝试执行"Math.floor(input_value / 4294967296)" (4294967296 = Math.pow(2, 32))时,我会得到"input_value"的高32位部分;我错过了什么吗? - snapfractalpop
你没有说明使用的编程语言,所以我假设你使用的是Java。'input_value'是一个长整型(即64位整数)吗?如果它是一个int(32位整数),那么左移32位的位运算将不起作用。 - Simon Howard
抱歉..我应该更明确一些。我正在使用JavaScript(在Node.js中),实际上很惊讶地发现将32位进行右移位操作后,竟然得到了相同的数字。我并不完全理解JavaScript中所有隐式类型转换及其底层表示。 - snapfractalpop
1
这仍然是一个JSON问题,而不是Javascript问题,因为JSON不支持超过9个小数位的整数。如果支持,那么Javascript实现将被迫在幕后提供支持。 - Lightness Races in Orbit
2
@LightnessRacesinOrbit,我似乎找不到关于9个小数位的声明的参考。这是在某种规范中吗? - akhaku
4
在我误解语法的时候,我在自己头脑中发明了规范(RFC 4627和www.json.org版本)中的这个内容。 - Lightness Races in Orbit

7

Javascript的Number类型(64位IEEE 754)只有大约53位的精度。

但是,如果您不需要进行任何加法或乘法运算,则可以将64位值保留为4个字符的字符串,因为JavaScript使用UTF-16。

例如,1可以编码为"\u0000\u0000\u0000\u0001"。这样做的好处是字符串的值比较(==、>、<)按预期工作。同时,似乎很容易编写位运算:

function and64(a,b) {
    var r = "";
    for (var i = 0; i < 4; i++)
        r += String.fromCharCode(a.charCodeAt(i) & b.charCodeAt(i));
    return r;
}

1
这是一种聪明的方法,既可以在传输数据时节省字节数,又可以更多或少地读取数字。如果采用这种方法是否存在任何问题,我很有兴趣知道。 - acjay

2

JS数字表示是标准的IEEE双精度浮点数,因此无法表示64位整数。如果我没记错的话,你可以在双精度浮点数中获得大约48位实际整数精度,但所有JS位操作都会减少到32位精度(这是规范要求的。耶!)所以,如果你真的需要在JS中使用64位整数,你将需要实现自己的64位整数逻辑库。


3
始终保持53位,不包括符号。 - quadrupleslap

0

JSON本身并不关心实现限制。 你的问题是JS无法处理你的数据,而不是协议的问题。 换句话说,你的JS客户端代码必须使用其中的任一非完美选项。


0

我遇到了这个问题。当通过json发送大整数到JSON.parse时,一切都变得混乱不堪。我花了几天时间来尝试调试。当我将值作为字符串传输时,问题立即解决。

使用 { "the_sequence_number": "20200707105904535" } 而不是 { "the_sequence_number": 20200707105904535 }

更糟糕的是,似乎每个实现JSON.parse的地方都是Firefox、Chrome和Opera之间共享的库,因为它们的行为完全相同。Opera的错误消息中有Chrome的URL引用,几乎像是WebKit被浏览器共享。

console.log('event_listen[' + global_weird_counter + ']: to be sure, server responded with [' + aresponsetxt + ']');
var response = JSON.parse(aresponsetxt);
console.log('event_listen[' + global_weird_counter + ']: after json parse: ' + JSON.stringify(response));

我遇到的问题是指针运算出了大问题,导致我的工作站上出现了各种幽灵,在我睡觉时制造混乱。现在我已经改用字符串,所有的幽灵都被驱逐了。


这不是一个 JSON 的问题,而是 Javascript 的问题。 - user48956
1
看起来是这样。请阅读 https://dev59.com/4Gkw5IYBdhLWcg3wyNkW - Desmond Coertzen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接