JavaScript 运行时如何将 BINARY(双精度浮点格式)转换回 DECIMAL

How does JavaScript runtime convert BINARY (Double-precision floating-point format) back to DECIMAL

给一个小数0.2

EX

var theNumber= 0.2;

我假设它会在内存中存储为(基于双精度 64 位浮点格式 IEEE 754)

0-01111111100-1001100110011001100110011001100110011001100110011001

该二进制数实际上四舍五入以适合 64 位。

如果我们取该值并将其转换回十进制,我们将得到

0.19999999999999998

(0.1999999999999999833466546306226518936455249786376953125)

不完全是 0.2

我的问题是,当我们要求 theNumber 的十进制值时(例如:alert(theNumber)),JavaScript 运行时如何知道 theNumber 最初是 0.2?

事实上,0.2 是由您发布的其他位序列表示的。
每次您的结果匹配正确的位序列时,控制台将输出 0.2。但是如果你的计算结果是其他顺序,控制台会输出类似你的0.19999999999999998的东西。

最常见的示例 0.1 + 0.2 也是类似的情况,它给出输出 0.30000000000000004,因为此结果的位序列与 0.3 的表示不同。

console.log(0.2)
console.log(0.05 + 0.15)
console.log(0.02 + 0.18)

console.log(0.3)
console.log(0.1 + 0.2)
console.log(0.05 + 0.25)

来自 ECMAScript 语言规范:

11.8.3.1 Static Semantics: MV
A numeric literal stands for a value of the Number type. This value is determined in two steps: first, a mathematical value (MV) is derived from the literal; second, this mathematical value is rounded [...(and here whole procedure is described)]

您可能还对以下部分感兴趣:

6.1.6 Number type
[...]
In this specification, the phrase “the Number value for x” where x represents an exact real mathematical quantity [...] means a Number value chosen in the following manner.
[...(whole procedure is described)]
(This procedure corresponds exactly to the behaviour of the IEEE 754-2008 “round to nearest, ties to even” mode.)

JavaScript 将 Number 到字符串的默认转换会产生足够的十进制数字来唯一区分 Number。 (这源于 ECMAScript 2018 Language Specification, which I explain a little here 的第 7.1.12.1 条中的第 5 步。)

让我们先考虑将十进制数字转换为 Number。将数字转换为 Number 时,其精确的数学值将四舍五入为 Number 中可表示的最接近的值。所以,当源代码中的0.2转换为Number时,结果是0.200000000000000011102230246251565404236316680908203125.

Number转换为十进制时,需要生成多少位数字才能唯一区分Number?在 0.200000000000000011102230246251565404236316680908203125 的情况下,如果我们产生“0.2”,我们有一个十进制数字,当再次转换为 Number 时,结果为 0.20000000000000001110223024168226356514因此,“0.2”唯一地将 0.200000000000000011102230246251565404236316680908203125 与其他 Number 值区分开来,所以这就是我们所需要的。

换句话说,JavaScript 生成刚好足以区分 Number 的数字的规则意味着任何短的十进制数字在转换为 Number 并返回字符串时将产生相同的十进制数字(除去不重要的零,因此“0.2000”将变为“0.2”或“045”将变为“45”)。 (一旦十进制数字变得足够长而与 Number 值发生冲突,它可能无法再进行往返转换。例如,“0.20000000000000003”将变为 Number 0.2000000000000000388578058618804789148271083831787109375,然后是字符串“ 0.20000000000000004”.)

If, as a result of arithmetic, we had a number close to 0.200000000000000011102230246251565404236316680908203125 but different, such as 0.2000000000000000388578058618804789148271083831787109375, then JavaScript will print more digits, “0.20000000000000004” in this case, because it needs more digits以区别于“0.2”的情况。

所以,我的假设是错误的。

我写了一个小程序来做实验。

进入内存的二进制值不是

0-01111111100-1001100110011001100110011001100110011001100110011001

尾数部分不是1001100110011001100110011001100110011001100110011001

之所以这样是因为我截断了值,而不是四舍五入。 :((

1001100110011001100110011001100110011001100110011001...[1001] 需要四舍五入到 52 位。如果系列为 1,则位 53,因此系列向上舍入并变为:1001100110011001100110011001100110011001100110011010

正确的二进制值应该是:

0-01111111100-1001100110011001100110011001100110011001100110011010

该值的完整小数是:

0.200 000 000 000 000 011 102 230 246 251 565 404 236 316 680 908 203 125

不是

0.199 999 999 999 999 983 346 654 630 622 651 893 645 524 978 637 695 312 5

正如 Eric 的回答,所有十进制数,如果都转换为二进制数

0-01111111100-1001100110011001100110011001100110011001100110011010

将 "seen" 为 0.2(除非我们使用 toFixed() 打印更多数字);所有这些十进制数字共享相同的二进制签名(我真的不知道如何描述它)。