所以我在处理大数字时试图理解JavaScript的行为。请考虑以下内容(在Firefox和Chrome中测试):
console.log(9007199254740993) // 9007199254740992
console.log(9007199254740994) // 9007199254740994
console.log(9007199254740995) // 9007199254740996
console.log(9007199254740996) // 9007199254740996
console.log(9007199254740997) // 9007199254740996
console.log(9007199254740998) // 9007199254740998
console.log(9007199254740999) // 9007199254741000
现在,我已经意识到为什么它会输出“错误的”错误。数字 - 它试图将它们转换为浮点表示并且它四舍五入到最接近的可能浮点值 - 但我不完全确定为什么它选择这些特定数字。我的猜测是,它试图绕到最近的“偶数”。数字,并且由于9007199254740996可以被4整除而9007199254740994不是,它认为9007199254740996更加“偶数”。
答案 0 :(得分:4)
正如Mark Dickinson在对该问题的评论中指出的那样,ECMA-262 ECMAScript语言规范要求使用IEEE 754 64位二进制浮点来表示Number Type。相关的舍入规则是"选择该组中与x最接近的成员。如果集合中的两个值同样接近,则选择具有偶数有效值的值..."。
这些规则是通用的,适用于算术的舍入结果以及文字的值。
以下是在IEEE 754 64位二进制浮点中可以准确表示的问题的相关范围内的所有数字。每个都显示为十进制值,也是其位模式的十六进制表示。具有偶数有效数的数字在其位模式中具有最右边的十六进制数字。
9007199254740992 bit pattern 0x4340000000000000
9007199254740994 bit pattern 0x4340000000000001
9007199254740996 bit pattern 0x4340000000000002
9007199254740998 bit pattern 0x4340000000000003
9007199254741000 bit pattern 0x4340000000000004
每个偶数输入都是这些数字中的一个,并且是该数字的四舍五入。每个奇数输入正好在它们中的两个之间,并且转向具有偶数有效数的一个。这导致将奇数输入四舍五入为9007199254740992,9007199254740996和9007199254741000。
答案 1 :(得分:1)
Patricia Shanahan answer帮了很多忙并解释了我的主要问题。然而,对于问题的第二部分 - 这种行为是否依赖于实现 - 事实证明它是肯定的,但是与我原先的想法略有不同。引自ECMA-262 5.1 § 7.8.3:
...舍入值必须是MV的Number值(在8.5中指定),除非文字是 DecimalLiteral 并且文字有超过20位有效数字,在这种情况下数字value可以是通过用 0 数字替换20之后的每个有效数字生成的文字的MV的数值,或者是通过替换每个有效数字生成的文字的MV的数值20, 0 数字,然后在第20位有效数字位置递增文字。
换句话说,实现可以选择忽略第20位之后的所有内容。考虑一下:
console.log(9007199254740993.00001)
Chrome和Firefox都会输出9007199254740994
,但是,Internet Explorer会输出9007199254740992
,因为它会选择忽略20位数后的数字。有趣的是,这似乎不符合标准的行为(至少在我阅读这个标准时)。它应该像9007199254740993.0001
一样解释它,但它不会。
答案 2 :(得分:0)
JavaScript将数字表示为64位浮点值。这在标准中定义。
http://en.wikipedia.org/wiki/Double-precision_floating-point_format
所以那里没有与中点四舍五入相关的事情。
作为提示,每个32位整数都具有双精度浮点格式的精确表示。
好的,既然您要求确切的算法,我就检查了Chrome的V8引擎是如何做到的。
V8定义了StringToDouble
函数,该函数在以下文件中调用InternalStringToDouble
:
https://github.com/v8/v8/blob/master/src/conversions-inl.h#L415
反过来,调用那里定义的Strotd
函数: