Javascript 中点';四舍五入';在处理大量数据时?
所以我试图理解JavaScript在处理大量数据时的行为。考虑以下内容(在Firefox和Chrome中测试): 现在,我知道为什么它输出了“错误”的数字,它试图将它们转换为浮点表示,并舍入到最接近的浮点值,但我不完全确定它为什么选择这些特定的数字。我的猜测是,它试图四舍五入到最近的“偶数”数字,因为9007199254740996可以被4整除,而9007199254740994不能,所以它认为9007199254740996更“偶数”Javascript 中点';四舍五入';在处理大量数据时?,javascript,floating-point,floating-point-precision,floating-point-conversion,Javascript,Floating Point,Floating Point Precision,Floating Point Conversion,所以我试图理解JavaScript在处理大量数据时的行为。考虑以下内容(在Firefox和Chrome中测试): 现在,我知道为什么它输出了“错误”的数字,它试图将它们转换为浮点表示,并舍入到最接近的浮点值,但我不完全确定它为什么选择这些特定的数字。我的猜测是,它试图四舍五入到最近的“偶数”数字,因为9007199254740996可以被4整除,而9007199254740994不能,所以它认为9007199254740996更“偶数” 它使用什么算法来确定内部表示?我猜这是常规中点舍入的扩展
- 它使用什么算法来确定内部表示?我猜这是常规中点舍入的扩展(舍入到偶数是IEEE 754函数中的默认舍入模式)
- 此行为是作为ECMAScript标准的一部分指定的,还是依赖于实现
好的,既然你要的是精确的算法,我检查了Chrome的V8引擎是如何做到的。 V8定义了一个
StringToDouble
函数,该函数调用以下文件中的InternalStringToDouble
:
这又调用了此处定义的Strotd
函数:
正如Mark Dickinson在对该问题的评论中指出的那样,ECMA-262 ECMAScript语言规范要求使用IEEE 754 64位二进制浮点来表示。相关的舍入规则是“选择该集合中与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。Patricia Shanahan的帮助很大,并解释了我的主要问题。然而,对于问题的第二部分,这个行为是否依赖于实现,结果是肯定的,但方式与我最初的想法略有不同。引述自:
…四舍五入值必须是MV的数值(如8.5中所规定),除非文字为小数位数且文字的有效位数超过20,在这种情况下,数值可以是通过用0数字替换第20个之后的每个有效数字而产生的文字的MV的数值,或者是通过用0数字替换第20个之后的每个有效数字然后增加文字而产生的文字的MV的数值在第20位有效数字位置
换句话说,实现可能会选择忽略第20位之后的所有内容。考虑这一点:
console.log(9007199254740993.00001)
Chrome和Firefox都将输出
9007199254740994
,然而,Internet Explorer将输出9007199254740992
,因为它选择忽略第20位之后的数字。有趣的是,这似乎不是符合标准的行为(至少在我阅读本标准时是这样)。它应将其解释为与9007199254740993.0001相同,但实际上并非如此。64位浮点最多可保留15位有效数字。9007199254740995是16位数字,因此没有精确的表示。@Lucastzesniewski在查找源代码方面做得很好。然而,即使通过算术(而不是字符串转换)得到数字,也会得到相同的舍入。例如,393447746243*22893
和950026289921*9481
都返回x=9007199254711000,即使实际答案分别是(x-1)和(x+1)。我也不知道发生了什么;只是提供了另一块拼图。@马特,就像我说的9007199254741000高于双打的最大精度,所以你不能真正期望那里的实际值。我刚刚检查过,您提供的两个乘法在C#中给出的值与在JS中的值相同,所以我想这就是CPU在硬件中进行计算的方式。@RobG:evaluationconsole.log(9007199254740993)
涉及两种转换:一种是将JavaScript代码中的十进制文字转换为内部二进制浮点格式,另一种是在执行log
方法时将内部二进制格式转换为控制台输出的十进制字符串。每个转换都需要一个非常复杂的算法(如果做得好的话)。所以说没有算法不是真的。是的,我认为这不是一个取整算法。是的,但取整是算法的一个重要部分:十进制到二进制的转换必须决定如何将精确的十进制值转换为IEEE 754 binary64格式表示的最接近的数字。这一决定的一部分涉及到选择哪种方式进行轮换,而正常的惯例是使用轮换关系来平衡,正如@PatriciaShanahan所描述的那样。事实证明,这不仅仅是“正常惯例”:ECMA-262标准明确规定,应使用舍入与均匀舍入的方法(见第8.5节)
console.log(9007199254740993.00001)