奇怪的JavaScript数字行为

4
我在处理JavaScript数字时发现了以下奇怪的行为。
var baseNum = Math.pow(2, 53);
console.log(baseNum); //prints 9007199254740992

console.log(baseNum + 1); //prints 9007199254740992 again!

console.log(baseNum + 2); //prints 9007199254740994, 2 more than +1

console.log(baseNum + 3) // prints 9007199254740996, 2 more than +2
console.log(baseNum + 4) // prints 9007199254740996, same as +3

这里发生了什么?我知道 JavaScript 只能表示最大数量为 2^53 的数字(它们在内部是“double”类型的),但为什么会出现这种情况?
如果 2^53 是实际上的最大值,那么为什么我们有 Number.MAX_VALUE1.7976931348623157e+308)呢?

var base,然后其他地方都用baseNum - Marc B
@MarcB 又被复制粘贴恶魔攻击了!已经修正错误。 - Ashwin Prabhu
2
文章详细内容请参见http://www.2ality.com/2012/04/number-encoding.html。 - Bill
@Bill 谢谢,这解释清楚了! - Ashwin Prabhu
3个回答

8

这个数字实际上是一个双精度浮点数。尾数有52位(关于双精度浮点数的来源和额外信息)。因此,存储2^53时会截断一位。

该数字使用3个部分存储:一个符号位(非常直观)和另外两个部分,即尾数M和指数E。该数字的计算方法如下:

(1 + M/2^53) * 2^(E-1023)

我可能有一些具体细节上的偏差,但基本思想是正确的。所以当数字为2^53时,2^(E-1023)=2^53,由于M中只有52位,因此不能再表示最低位。


谢谢,我明白了。但是我的抱怨是关于添加1/2/3...的行为。你能解释一下吗? - Ashwin Prabhu
1
@AshwinPrabhu 它确实解释了,不是吗? 因为在超过 2^53 后,它不能再每秒钟增加1了-- 它会以2的次幂计数...直到变得更大和不准确并以4的次幂计数,依此类推。 - Nicole

3
@CrazyCasta给出的答案很好。唯一需要补充的是针对你的第二个问题:

如果2^53是实际最大值,那么为什么我们有Number.MAX_VALUE(1.7976931348623157e+308)呢?

正如你所证明的,它可以存储比2^53更大的数字,但精度比2^0差。随着数字越来越大,它们失去的精度也越来越多。
因此,Number.MAX_VALUE中的最大值表示它可以表示的“最大”值;但这并不意味着精度与接近2^12^53的值相同。
这个结论的推论是Number.MIN_VALUE是Number可以包含的最小值;而不是最小负数。也就是说,它是靠近零的最接近于非零的数字:5.00E-324(注意这是一个正数!)。

2
这是一个类比:你在纽约市中心,被告知到伦敦中心的距离是3465英里。现在你向西走了一步,到达另一个位置,但到伦敦的距离仍然是3465英里。由于这个测量精度是以英里为单位,所以即使加上几英寸或几英尺也不会改变它。 - Barmar

1
在long类型中可存储的最大值远大于可在long类型中以精确精度存储的最大值。浮点数具有固定数量的有效数字,但数字的大小可以更大。指数(二次幂)分配了一定数量的位数,并且隐式地与存储在其余位数中的尾数相乘。超过一定点后,你会失去精度,但你可以继续增加指数以表示更大的数字大小。

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