问题是数字表示的根本问题 - 任何 超过Number.MAX_SAFE_INTEGER 的数字不再安全使用。基本示例:
const max = Number.MAX_SAFE_INTEGER;
const maxPlus1 = Number.MAX_SAFE_INTEGER + 1;
const maxPlus2 = Number.MAX_SAFE_INTEGER + 2;
console.log("max:", max); //9007199254740991
console.log("max + 1:", maxPlus1); //9007199254740992
console.log("max + 2:", maxPlus2); //9007199254740992
console.log("max + 1 = max + 2:", maxPlus1 === maxPlus2); //true
如您所见,几乎在您突破Number.MAX_SAFE_INTEGER 障碍之后,您就会遇到精度问题。 JavaScript 使用 IEEE 754 standard 来表示数字,虽然它可以显示比其最高值的数字(与其他语言中的 int 字段相反,它将溢出到零或最大负数),这样的表示是不精确。一些大数字不能像9007199254740993(即Number.MAX_SAFE_INTEGER + 2)那样表示,而是得到一个不同的数字。
同样的事情也适用于parseInt,因为它将字符串转换为 JavaScript 数字,可能没有精确的表示:
const maxPlus1String = "9007199254740992";
const maxPlus2String = "9007199254740993";
const maxPlus1 = parseInt(maxPlus1String);
const maxPlus2 = parseInt(maxPlus2String);
console.log("max + 1:", maxPlus1); //9007199254740992
console.log("max + 2:", maxPlus2); //9007199254740992
console.log("(string) max + 1 = max + 2:", maxPlus1String === maxPlus2String); //false
console.log("max + 1 = max + 2:", maxPlus1 === maxPlus2); //true
最终,这是一个浮点数如何表示的问题。 Wikipedia has a good article 但我会将其简化为最重要的部分:
使用浮点表示,您可以保留 尾数(也称为 significand,末尾带有 d)和 exponent em> 为每个数字。这就像科学记数法一样工作,所以我将使用它以便于参考:
1.23e5 = 1.23 * 105 = 123 000
使用这两种方法,您可以用非常短的形式表示任意高的数字。但是,使用浮点表示,您可以保留每个位的位数。这是以牺牲准确性为代价的,一旦你用完尾数的数字,你就会失去准确性。因此,如果我们决定在科学记数法中只允许一位小数,我们会得到数字1.2e5,可能是 123 000,但也可能是 120 000 或 125 000 或 128 215 - 我们无法从缩短的形式重新创建它。浮点数也会发生类似的情况——一旦你没有足够的尾数位数,其余的就会被丢弃,所以你不会得到确切的数字。
当指数用完数字时,您会达到可表示的最高数字。
在 JavaScript 中,可能的最大数量可以在Number.MAX_VALUE 中看到:
console.log(Number.MAX_VALUE)
1.7976931348623157e+308相当大,指数为308。所以你可以用这个来表示很多数字,如果你在这个值下使用parseInt,你会得到在你解析的区域内的一些数字。
但是,如果您超过会发生什么?好吧,您将获得一个在 JavaScript 中可表示的数字范围内的值,该值是出于特殊原因而保留的。这是 绝对 最高数 - 一个浮点表示,表示可能表示的最大数。该值为Infinity。如果您碰巧解析了大于Number.MAX_VALUE 的内容,您将得到Infinity:
const largeNum = "17" + "0".repeat(307); //1.7e308
const tooLargeNum = "18" + "0".repeat(307); //1.8e308
console.log("large number string:", largeNum);
console.log("large number parsed:", parseInt(largeNum));
console.log("too large number string:", tooLargeNum);
console.log("too large number parsed:", parseInt(tooLargeNum));
因此,即使您有天文数字,您也可以保证有一个大于零的数字,因为Infinity > 0