JavaScript如何确定格式化浮点值时生成的位数?

How does JavaScript determine the number of digits to produce when formatting floating-point values?

在javascript中,每个人都知道著名的计算:0.1 + 0.2 = 0.30000000000000004。但是为什么javascript会打印这个值而不是更精确的0.300000000000000044408920985006


Number值转换为十进制数字时,javascript的默认规则是使用足够的数字来区分Number值。(您可以使用toPrecision方法请求更多或更少的数字。)

javascript使用ieee-754基本64位二进制浮点作为其Number类型。使用ieee-754,.1 + .2的结果正好是0.30000000000000044408920985006266169452667236328125。这是由于:

  • 将".1"转换为可在Number类型中表示的最接近的值。
  • 将".2"转换为可在Number类型中表示的最接近的值。
  • 将上述两个值相加,并将结果四舍五入到Number类型中可表示的最近值。

当格式化显示的Number值时,"0.30000000000000004"只有足够的有效数字来唯一地区分该值。要看到这一点,请注意相邻值是:

  • 0.299999999999999988897769753748434595763683319091796875
  • 0.3000000000000000444089209850062616169452667236328125
  • 江户十一〔11〕。

如果转换为十进制数字只产生"0.3000000000000000",则它将接近于0.29999999999988897769753748434595763683319091796875,而不是0.300000000000000444089208500626616945267236328125。因此,需要另一个数字。当我们得到这个数字"0.30000000000000004"时,结果比它的任何一个邻居都更接近于0.300000000004440892098500626616945267236328125。因此,"0.300000000004"是最短的十进制数字(忽略了前面的"0",这是出于审美目的),它唯一地区分了原始值可能是哪个值。

本规则来自ECMAScript 2017语言规范第7.1.12.1条第5步,这是将Number值m转换为ToString操作的十进制数字的步骤之一:

Otherwise, let n, k, and s be integers such that k ≥ 1, 10k‐1 ≤ s < 10k, the Number value for s × 10n‐k is m, and k is as small as possible.

这里的措词有点不精确。我花了一段时间才知道,通过"s×10n-k的数值",标准是指Number值,这是将数学值s×10n-k转换为Number类型(通常是四舍五入)的结果。在这个描述中,k是将要使用的有效位数,这个步骤告诉我们要最小化k,所以它说使用最小位数,这样我们产生的数字在转换回Number类型时,会产生原始数字m。