java - NaN 的位模式真的依赖于硬件吗?

标签 java floating-point nan ieee-754

我正在阅读 Java 语言规范中的浮点 NaN 值(我很无聊)。 32 位 float 具有这种位格式:

seee eeee emmm mmmm mmmm mmmm mmmm mmmm

s 是符号位,e 是指数位,m 是尾数位。 NaN 值被编码为全 1 的指数,并且尾数位不全为 0(这将是 +/- 无穷大)。这意味着有许多不同的可能 NaN 值(具有不同的 sm 位值)。

对此,JLS §4.2.3说:

IEEE 754 allows multiple distinct NaN values for each of its single and double floating-point formats. While each hardware architecture returns a particular bit pattern for NaN when a new NaN is generated, a programmer can also create NaNs with different bit patterns to encode, for example, retrospective diagnostic information.

JLS 中的文本似乎暗示,例如,0.0/0.0 的结果具有与硬件相关的位模式,并且取决于该表达式是否被计算为编译时间常量,它所依赖的硬件可能是编译 Java 程序的硬件或运行程序的硬件。如果属实,这一切似乎非常不稳定。

我进行了以下测试:

System.out.println(Integer.toHexString(Float.floatToRawIntBits(0.0f/0.0f)));
System.out.println(Integer.toHexString(Float.floatToRawIntBits(Float.NaN)));
System.out.println(Long.toHexString(Double.doubleToRawLongBits(0.0d/0.0d)));
System.out.println(Long.toHexString(Double.doubleToRawLongBits(Double.NaN)));

我机器上的输出是:

7fc00000
7fc00000
7ff8000000000000
7ff8000000000000

输出没有显示任何超出预期的内容。指数位都是 1。尾数的高位也是 1,这对于 NaN 显然表示“安静的 NaN”,而不是“发信号的 NaN”(https://en.wikipedia.org/wiki/NaN#Floating_point)。符号位和尾数位的其余部分为 0。输出还显示,在我的机器上生成的 NaN 与 Float 和 Double 类中的常量 NaN 没有区别。

我的问题是,无论编译器或虚拟机的 CPU 是多少,Java 是否都能保证输出,还是真的无法预测? JLS 对此很神秘。

如果 0.0/0.0 保证该输出,是否有任何算术方法可以生成具有其他(可能与硬件相关?)位模式的 NaN? (我知道 intBitsToFloat/longBitsToDouble 可以编码其他 NaN,但我想知道其他值是否可以从正常算术中产生。)


后续要点:我注意到 Float.NaNDouble.NaN指定它们的确切位模式,但在源代码(FloatDouble)中,它们是由 0.0/0.0 生成的。如果该划分的结果确实取决于编译器的硬件,那么无论是规范还是实现似乎都存在缺陷。

最佳答案

这就是 §2.3.2 of the JVM 7 spec不得不说:

The elements of the double value set are exactly the values that can be represented using the double floating-point format defined in the IEEE 754 standard, except that there is only one NaN value (IEEE 754 specifies 253-2 distinct NaN values).

§2.8.1 :

The Java Virtual Machine has no signaling NaN value.

所以从技术上讲,只有一个 NaN。但是§4.2.3 of the JLS还说(在你的报价之后):

For the most part, the Java SE platform treats NaN values of a given type as though collapsed into a single canonical value, and hence this specification normally refers to an arbitrary NaN as though to a canonical value.

However, version 1.3 of the Java SE platform introduced methods enabling the programmer to distinguish between NaN values: the Float.floatToRawIntBits and Double.doubleToRawLongBits methods. The interested reader is referred to the specifications for the Float and Double classes for more information.

我认为这正是你和 CandiedOrange 的意思提议:它依赖于底层处理器,但 Java 对待它们都是一样的。

但它变得更好:显然,您的 NaN 值完全有可能被静默转换为不同的 NaN,如 Double.longBitsToDouble() 中所述。 :

Note that this method may not be able to return a double NaN with exactly same bit pattern as the long argument. IEEE 754 distinguishes between two kinds of NaNs, quiet NaNs and signaling NaNs. The differences between the two kinds of NaN are generally not visible in Java. Arithmetic operations on signaling NaNs turn them into quiet NaNs with a different, but often similar, bit pattern. However, on some processors merely copying a signaling NaN also performs that conversion. In particular, copying a signaling NaN to return it to the calling method may perform this conversion. So longBitsToDouble may not be able to return a double with a signaling NaN bit pattern. Consequently, for some long values, doubleToRawLongBits(longBitsToDouble(start)) may not equal start. Moreover, which particular bit patterns represent signaling NaNs is platform dependent; although all NaN bit patterns, quiet or signaling, must be in the NaN range identified above.

作为引用,有一个硬件相关的 NaN 表 here .总结:

- x86:     
   quiet:      Sign=0  Exp=0x7ff  Frac=0x80000
   signalling: Sign=0  Exp=0x7ff  Frac=0x40000
- PA-RISC:               
   quiet:      Sign=0  Exp=0x7ff  Frac=0x40000
   signalling: Sign=0  Exp=0x7ff  Frac=0x80000
- Power:
   quiet:      Sign=0  Exp=0x7ff  Frac=0x80000
   signalling: Sign=0  Exp=0x7ff  Frac=0x5555555500055555
- Alpha:
   quiet:      Sign=0  Exp=0      Frac=0xfff8000000000000
   signalling: Sign=1  Exp=0x2aa  Frac=0x7ff5555500055555

因此,要验证这一点,您确实需要这些处理器之一并尝试一下。此外,欢迎任何关于如何解释 Power 和 Alpha 架构的较长值的见解。

关于java - NaN 的位模式真的依赖于硬件吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25050133/

相关文章:

python - Pandas crosstab() 函数与包含 NaN 值的数据框的混淆行为

java - 当我尝试在 Spring 中下载 CSV 时获得 CPU 100%

java - 使用 Java 8 计算列表中的出现次数

string - MATLAB 中具有特定格式的 num2str

c# - 闯入 C# 调试器以除以零

python - 优雅的 numpy 数组移位和 NaN 填充?

java - 如何设置MySQLDataSource的池大小?

java - 第一个 tomcat 应用程序 - 'ant install' 错误 "NoClassDefFound"

bash - 将浮点变量转换为整数?

javascript - 如何在 Jasmine 中比较两个具有浮点值的对象?