您超出了 JavaScriptnumber
类型的容量,有关详细信息,请参阅规范的第 8.5 节。这些 ID 需要是字符串。
IEEE-754 双精度浮点(JavaScript 使用的那种数字)不能精确地表示所有数字(当然)。众所周知,0.1 + 0.2 == 0.3
是假的。这会影响整数,就像它影响分数一样。一旦超过 9,007,199,254,740,991 ( Number.MAX_SAFE_INTEGER
),它就会开始。
超出Number.MAX_SAFE_INTEGER + 1
( 9007199254740992
) 后,IEEE-754 浮点格式不能再表示每个连续的整数。9007199254740991 + 1
是,9007199254740992
但9007199254740992 + 1
也是因为 不能用格式表示。下一个可以是。那么不可能,但可以。9007199254740992
9007199254740993
9007199254740994
9007199254740995
9007199254740996
原因是我们的比特已经用完了,所以我们不再有一个 1 的比特;最低位现在代表 2 的倍数。最终,如果我们继续前进,我们会丢失该位,只能以 4 的倍数工作。依此类推。
您的值远高于该阈值,因此它们会四舍五入到最接近的可表示值。
从 ES2020 开始,您可以使用BigInt
任意大的整数,但它们没有 JSON 表示。您可以使用字符串和 reviver 函数:
const jsonString = '{"id":"714341252076979033","type":"FUZZY"}';
// Note it's a string −−−−^−−−−−−−−−−−−−−−−−−^
const obj = JSON.parse(jsonString, (key, value) => {
if (key === "id" && typeof value === "string" && value.match(/^\d+$/)) {
return BigInt(value);
}
return value;
});
console.log(obj);
(Look in the real console, the snippets console doesn't understand BigInt.)
如果您对这些位感到好奇,会发生以下情况:一个 IEEE-754 二进制双精度浮点数有一个符号位,11 位指数(它定义了数字的整体规模,作为 2 的幂 [因为这是一种二进制格式])和 52 位有效位(但该格式非常聪明,它从这 52 位中获得了 53 位的精度)。指数的使用方式很复杂(在此处描述),但用非常模糊的术语来说,如果我们在指数上加一,则有效数字的值加倍,因为指数用于 2 的幂(再次警告,它是不直接,那里有聪明之处)。
所以让我们看一下值9007199254740991
(又名,Number.MAX_SAFE_INTEGER
):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−− 符号位
/ +−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−-−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−− 指数
// | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− +− 有效数
// | / |
0 10000110011 111111111111111111111111111111111111111111111111111
= 9007199254740991 (Number.MAX_SAFE_INTEGER)
那个指数值 ,10000110011
意味着每次我们在有效位上加一,表示的数字就会增加 1(整数 1,我们更早失去了表示小数的能力)。
但是现在那个有效位已经满了。要超过这个数字,我们必须增加指数,这意味着如果我们在有效位上加 1,则表示的数字的值会增加 2,而不是 1(因为指数应用于 2,所以它的底数二进制浮点数):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−− 符号位
/ +−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−-−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−− 指数
// | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− +− 有效数
// | / |
0 10000110100 00000000000000000000000000000000000000000000000000000
= 9007199254740992 (Number.MAX_SAFE_INTEGER + 1)
好吧,没关系,因为9007199254740991 + 1
无论如何9007199254740992
。但!我们不能代表9007199254740993
。我们已经用完了比特。如果我们只在有效位上加 1,它会在值上加 2:
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−− 符号位
/ +−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−-−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−− 指数
// | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− +− 有效数
// | / |
0 10000110100 00000000000000000000000000000000000000000000000000001
= 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)
当我们增加值时,格式不再代表奇数,指数太大了。
最终,我们再次用完了有效位,必须增加指数,所以我们最终只能表示 4 的倍数,然后是 8 的倍数,然后是 16 的倍数,以此类推。