你正在超出JavaScript的number类型的容量,详见
规范第8.5节和
IEEE-754双精度二进制浮点数的维基百科页面。这些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表示形式。您可以使用字符串和一个恢复函数:
const jsonString = '{"id":"714341252076979033","type":"FUZZY"}';
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
)的情况:
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 符号位
/ +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
那个指数值,
10000110011
,意味着每次我们给尾数加一,所代表的数值就会增加1(整数1,我们早就失去了表示小数的能力)。
但是现在尾数已经满了。要超过这个数,我们必须增加指数,这意味着如果我们给尾数加一,所代表的数值就会增加2,而不是1(因为指数应用于2,这个二进制浮点数的基数)。
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 符号位
/ +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
好吧,没关系,因为
9007199254740991 + 1
无论如何都是
9007199254740992
。但是!我们无法表示
9007199254740993
。我们已经用完了比特位。如果我们只是将有效数字加1,那么值就会增加2:
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 符号位
/ +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
最终,我们再次用尽了有效数字位数,不得不增加指数,因此我们最终只能表示4的倍数。然后是8的倍数。然后是16的倍数。以此类推。