为什么Number()在处理非常大的整数时会返回错误的值?

6

Number()函数在某些参数上返回不正确的值,比如这样:

Number('10000000712224641') returns 10000000712224640
Number('10000000544563531') returns 10000000544563532

我在Firefox、Chrome、IE和Node.js上测试了这个。为什么会发生这种情况?

2
对浮点精度的赌注。 - Kyll
1
JavaScript(以及其他所有编程语言)都有充分记录的示例来解决此类问题。 - arthurakay
3
在JavaScript中,所有数字都是以相同的方式存储的吗? - Kyll
2
@JuanMendes:对我来说,这肯定是浮点精度问题。溢出会导致值变为负数或者其他极其错误的值。 - Jon Skeet
@JonSkeet 第一次向我发表意见... 他审查了我的答案并解释说,一些大于 Number.MAX_SAFE_INTEGER 的整数可以被表示,并且这是由于精度问题。 - Ruan Mendes
3个回答

10

JavaScript 安全地支持约17位数字,所有数字(无论是浮点数还是整数)都以64位IEEE-754二进制浮点数表示。

Number.MAX_SAFE_INTEGER // 9007199254740991

当您超过该数字时,尾数将被四舍五入,除非您具有2的幂(或2的幂相加)

 Math.pow(2, 54)     // 18014398509481984 (not rounded)
 Math.pow(2, 54) + 1 // 18014398509481984 (rounded)
 Math.pow(2, 54) - 1 // 18014398509481984 (rounded)
 Math.pow(2,57) + Math.pow(2,52) // 148618787703226370 (not rounded)
 Math.pow(2, 57) + Math.pow(2, 52) + 1 // 148618787703226370 (rounded)

1
这不是2的准确幂的问题...只是因为有53位精度(假设一个标准化的数字)。因此,任何只需要53位有效数字(并且在正确的指数范围内)的整数都可以被精确地表示。 - Jon Skeet
@JonSkeet 我正在尝试理解 Math.pow(2,54) 是如何工作的,以及所有的 Math.pow(2,54) -1 === Math.pow(2,54)Math.pow(2,54) + 1 === Math.pow(2,54) - Ruan Mendes
当你只有53个有效位可用时,思考一下2^54+1的二进制表示是什么。最接近完全可表示的值是... 2^54。 - Jon Skeet
@JonSkeet 我仍在努力理解你的意思,我说过2的幂次方即使大于Number.MAX_SAFE_INTEGER也可以精确表示,而其他数字则不行。这与你所说的有何不同? - Ruan Mendes
1
考虑2^54 + 2^52。这不是2的幂,这意味着根据您的规则它不能被精确表示 - 但实际上它可以,因为它不需要超过53个有效数字来精确表示它。 - Jon Skeet
@JonSkeet 现在答案看起来准确吗? - Ruan Mendes

7

JavaScript使用64位IEEE-754二进制浮点数来存储所有数字 - 就像C#和Java中的double一样。没有不同的类型来存储整数。(实际实现可能使用优化方式避免总是以这种方式执行算术运算,但从最终用户的角度来看,结果将始终被视为64位二进制浮点值处理。)

这意味着只有52位可用于存储有效数字,其他位用于指数和符号。通过规范化,这意味着您可以有效地存储具有53个有效位精度的值。这意味着超过2 53 -1(这是其他答案中引用的值9007199254740991),"相邻"数字之间的距离大于1,因此无法精确存储所有整数。


引擎层面上并没有不同的类型来储存整数。至少在V8中,有一个专门用于小整数(31位)的Smi类型。所以像x = 3 + 2这样的操作在内部不会涉及到浮点数。 - georg
@georg: 我只能找到旧的 ECMA-262 规范(ECMAscript 而非 JS...)所以我按照那个来做了。欢迎提供更多建议 :) 这种优化对用户可见吗,或者他们总是可以将数字视为始终存储为 64 位 IEEE-754? - Jon Skeet
它完全透明,一个JS程序不可能区分SMI(即时)和双精度堆分配的数字。而ECMAscript是JS的(官方方言),2015规范在此处:http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types-number-type。 - georg
@georg:好的,很高兴这只是一个实现细节 :) - Jon Skeet

2

这是因为javascript支持许多数字。最大安全整数存储在一个名为MAX_SAFE_INTEGER的常量中,其值为9007199254740991。


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