4

所以我试图理解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更“平均”。

  • 它使用什么算法来确定内部表示?我的猜测是它是规则中点舍入的扩展(舍入到甚至是IEEE 754函数中的默认舍入模式)。
  • 此行为是否被指定为ECMAScript标准的一部分,还是与实施相关?
+0

@RobG:评估'console.log(9007199254740993)'涉及两次转换:一次是将JavaScript代码中的十进制文字转换为内部二进制浮点格式,另一次是将内部二进制格式转换为十进制执行'log'方法时用于控制台输出的字符串。这些转换中的每一个都需要相当复杂的算法(如果做得好的话)。所以说没有算法是不正确的。 –

+0

是的,认为这不是一个舍入算法。 – RobG

+0

确实如此,但舍入是算法的重要组成部分:十进制到二进制转换必须决定如何将精确的十进制值转换为可用IEEE 754 binary64格式表示的最接近的数字。该决定的一部分涉及选择以哪种方式进行取整,而常规惯例是使用环绕式转换,如@PatriciaShanahan所述。 –

回答

4

正如马克指出, 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.

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功能:

https://github.com/v8/v8/blob/master/src/strtod.cc

+0

一个64位浮点数可以保持高达15位有效数字。 9007199254740995是16位数字,因此没有确切的表示形式。 –

+0

@LucasTrzesniewski不错的工作,寻找源代码。但是,即使通过算术(而不是字符串转换)达到数字也会得到相同的舍入。例如。即使真实答案分别是(x-1)和(x + 1),“393447746243 * 22893”和“950026289921 * 9481”都返回x = 9007199254741000。我也不知道发生了什么;只是提供另一块拼图。 – Matt

+0

@Matt就像我说的9007199254741000高于双打的最高精度,你不能真正期待那里的真实值。我只是检查了一下,你提供的两个乘法在C#中的值与JS相同,所以我想这就是CPU如何在硬件中进行计算。 –

1

帕特里夏·沙纳汉answer帮了不少忙,并解释我的首要问题。然而,对于问题的第二部分 - 这种行为是否依赖于实现而言 - 事实证明,它的确如此,但与我原先认为的略有不同。引用自ECMA-262 5.1 § 7.8.3

…四舍五入的值必须是MV的数值(如8.5所指定),除非文字是DecimalLiteral并且文字具有超过20位有效数字,在这种情况下,Number值可能是Number的值通过将第20个之后的每个有效数字替换为数字,或者通过将第20个之后的每个有效数字替换为数字,然后将文字递增第20位有效数字位置。

换句话说,实现可能会选择忽略20位数后的所有内容。试想一下:

console.log(9007199254740993.00001) 

器和Firefox将输出9007199254740994,但是,Internet Explorer将输出9007199254740992因为它选择了20位之后忽略。有趣的是,这似乎不符合标准的行为(至少在我读这个标准时)。它应该解释这与9007199254740993.0001相同,但它不。