我正在编写与 R 交互的 Java 代码,其中“NA”值与 NaN 值不同。 NA 表示一个值“统计缺失”,即无法收集或不可用。
class DoubleVector {
public static final double NA = Double.longBitsToDouble(0x7ff0000000001954L);
public static boolean isNA(double input) {
return Double.doubleToRawLongBits(input) == Double.doubleToRawLongBits(NA);
}
/// ...
}
以下单元测试演示了 NaN 和 NA 之间的关系,并且在我的 Windows 笔记本电脑上运行良好,但“isNA(NA) #2”在我的 ubuntu 工作站上有时失败。
@Test
public void test() {
assertFalse("isNA(NaN) #1", DoubleVector.isNA(DoubleVector.NaN));
assertTrue("isNaN(NaN)", Double.isNaN(DoubleVector.NaN));
assertTrue("isNaN(NA)", Double.isNaN(DoubleVector.NA));
assertTrue("isNA(NA) #2", DoubleVector.isNA(DoubleVector.NA));
assertFalse("isNA(NaN)", DoubleVector.isNA(DoubleVector.NaN));
}
从调试来看,DoubleVector.NA 似乎已更改为规范的 NaN 值 7ff8000000000000L,但很难判断,因为将其打印到标准输出给出的值与调试器提供的值不同。
此外,只有在之前的许多其他测试之后运行测试才会失败;如果我单独运行此测试,它总是会通过。
这是 JVM 错误吗?优化的副作用?
测试总是通过:
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) Client VM (build 19.1-b02, mixed mode, sharing)
测试有时会失败:
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode)
最佳答案
您正踏入非常危险的水域,这是 Java VM 行为未完全指定的少数区域之一。
根据 JVM 规范,double
范围内只有“一个 NaN 值”。对 double 的算术运算无法区分两个不同的 NaN
值。
The documentation of 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. SolongBitsToDouble
may not be able to return a double with a signaling NaN bit pattern. Consequently, for some long values,doubleToRawLongBits(longBitsToDouble(start))
may not equalstart
. 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.
因此假设处理 double
值将始终保持特定 NaN
值不变是一件危险的事情。
最干净 的解决方案是将您的数据存储在long
中,并在检查您的特殊值(value)。但是,这将对性能产生非常显着的影响。
您可能通过在受影响的地方添加strictfp
标志来逃脱。这不会以任何方式保证它会工作,但它会(可能)改变您的 JVM 处理浮点值的方式并且可能只是必要的提示,有助于.但是,它仍然不可移植。
关于java - 最终的、非规范的 NaN double 值在运行时发生变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6371965/