Number
x时,这些规则会生成最短的十进制数字(在其有效数字中,不计入前导“0.”等装饰),以便在转换回Number
格式时产生x。此规则的目的包括(a)始终确保显示唯一标识源值是哪个确切的Number
值和(b)不使用比实现(a)更多的数字。因此,如果您从诸如.57之类的十进制数字开始并将其转换为Number
,则会得到某个值x,该值是由于转换必须四舍五入为可表示为Number
格式的数字而产生的结果。然后,当对x进行格式化以供显示时,您将获得原始数字,因为规则表明要生成最短的数字,以便转换回x自然地生成您开始使用的数字。
(但是这个x并不精确地代表0.57。最接近0.57的double
略低于它;请参阅IEEE double
计算器上它的十进制和二进制64表示)。
另一方面,当你执行某些操作,比如.57 + 1
,你正在进行一些算术运算,产生了一个起始不是简单十进制数字的数y。因此,在为显示格式化这样一个数字时,规则可能需要使用更多的数字。换句话说,当你加上.57
和1
时,在Number
格式中得到的结果并不是你从1.57
得到的相同数字。所以,为了格式化.57 + 1
的结果,JavaScript必须使用更多的数字来区分那个数字和你从1.57
得到的数字——它们不同,必须以不同的方式显示。
double
,则求和的预舍入结果将完全是1.57,因此1 + 0.57
将舍入到与1.57
相同的double
。1 + nearest_double(0.57)
=1.569999999999999951150186916493
(预舍入,不是double
),它向下舍入为1.56999999999999984012788445398
。这些数字表示法比我们需要区分尾数的1ulp(最后一位单位)甚至0.5 ulp最大舍入误差所需的位数多得多。
1.57
四舍五入得到 ~1.57000000000000006217248937901,因此不能用它来打印 1 + 0.57
的结果。该十进制字符串需要将该数字与相邻的binary64值区分开。
恰巧,.55 + 1
中出现的四舍五入与将 1.55
转换为 Number
后得到的数字相同,因此显示 .55 + 1
的结果将产生“1.55”。
double
并不完全代表该数字;有些人可能会忽略打印为0.57
并不意味着它完全是0.57
这一事实。这就是为什么1 + 0.57
可能与1.57
不同的原因;如果0.57
可以被精确存储,那么需要四舍五入到最接近的double的总和将完全是1.57
,与十进制字面值相同。 - Peter Cordesdouble(0.57)
、1 + double(0.57)
(在转换为 double
前后进行四舍五入)和 double(1.57)
的十进制表示。 - Peter CordestoString(2)
会打印出最后一个非零位之前的字符串。
1.57与1 + 0.57的二进制表示不同(但是得到1.57的结果并不是不可能的),
但是在二进制中,1 + 0.55等于1.55,如下面的代码片段所示:
console.log(1.57)
console.log(1.57.toString(2))
console.log((1+.57).toString(2))
console.log("1.32 + 0.25 = ",1.32 + .25)
console.log((1.32 + .25).toString(2))
console.log(1.55)
console.log(1.55.toString(2))
console.log((1+.55).toString(2))
请记住计算机是基于二进制数进行运算的,1.57
或 1.55
只是人类可以读懂的输出结果。
Number.prototype.toString
粗略地实现了ES262规范的以下部分:toString
只是估计值,它不返回存储的确切字节。
Number
格式及其算术运算。这个问题中的操作在不同正确实现的 JavaScript 中并没有区别。 - Eric Postpischil