是否允许 JS 引擎更改 NaN 的位?

Are JS engines allowed to change the bits of a NaN?

在JavaScript中,NaN值可以在内部用范围广泛的64位双精度表示。具体来说,任何具有以下按位表示的双精度数:

x111 1111 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

被解释为 NaN。我的问题是:假设我使用 ArrayBuffers 将两个 32 位 uint 转换为 JS Number,传递它,然后将其转换回两个 32 位 uint。恢复后的位是否与原始位相同,还是允许 JS 引擎随意更改 NaN 的位?也就是说,JS数可以用来无损存储64位吗?

ECMA-262 第 9th 版,2018 年 6 月,(JavaScript 旨在符合的标准)在 6.1.6“数字类型”:

… the 9007199254740990 (that is, 253-2) distinct “Not-a-Number” values of the IEEE Standard are represented in ECMAScript as a single special NaN value.… In some implementations, external code might be able to detect a difference between various Not-a-Number values, but such behaviour is implementation-dependent; to ECMAScript code, all NaN values are indistinguishable from each other.

24.1.17“NumberToRawBytes(类型、值、isLittleEndian)”说:

… If value is NaN, rawBytes may be set to any implementation chosen IEEE 754-2008 binary64 format Not-a-Number encoding. An implementation must always choose the same encoding for each implementation distinguishable NaN value.…

我没有看到任何其他提到 NaN 的段落可以阐明这个问题。一方面,24.1.17 有效地告诉我们在将 NaN 转换为原始字节时必须保留 NaN 的位。然而,似乎没有任何其他信息告诉我们必须在其他操作中保留这些位。人们可能会推断这是意图,因为如果这些位可以被任何其他操作任意更改,则 24.1.17 中的这一要求将毫无用处。但我不会依赖 JavaScript 实现来按照该意图实现它。

我曾经向 a question 询问 Java,关于 NaN 值的硬件依赖性,我注意到一些 CPU 会默默地将 "signaling NaN" 转换为 "quiet NaN"(设置安静的 NaN 位)当 NaN 值加载到处理器寄存器中时。 所以至少有一个位,安静的 NaN 位,不能用于存储任意数据。

使用其他位,只要设置了 quiet NaN 位,可能 是安全的。但是这里似乎仍然存在依赖于实现的空间,因此不能保证。

这类问题就是为什么正常的语言操作会避免做任何依赖于 NaN 内部值的事情,而更愿意将所有 NaN 视为 "just NaN"。

最初的 IEEE-754 标准故意将 NaN 的位留给实现。它确实提供了提示,例如

您可以放置​​创建 NaN 的原始内存地址。

同时,算术对于如何处理 NaN 有特定的规则,而这与底部的位无关。我认为它甚至没有说明在添加两个 NaN 时要做什么——保留其中一个的位而不是组成另一组位。只是结果必须仍然是 NaN。