我正在编写与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);
}
/// ...
}
Run Code Online (Sandbox Code Playgroud)
以下单元测试演示了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));
}
Run Code Online (Sandbox Code Playgroud)
从调试开始,看起来DoubleVector.NA被更改为规范的NaN值7ff8000000000000L,但很难分辨,因为将它打印到stdout会给出与调试器不同的值.
此外,如果测试在之前的许多其他测试之后运行,则测试仅失败; 如果我单独运行这个测试,它总是通过.
这是一个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)
Run Code Online (Sandbox Code Playgroud)
测试有时会失败:
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)
Run Code Online (Sandbox Code Playgroud)
您正在这里非常危险的水中行走,这是少数几个没有精确指定Java VM行为的区域之一.
根据JVM规范,该double
范围内只有"NaN值" .对双精度数没有算术运算可以区分两个不同的NaN
值.
文档longBitsToDouble()
有这个说明:
请注意,此方法可能无法返回具有
double
与long参数完全相同的位模式的NaN.IEEE 754区分了两种NaN,即安静的NaN和信号NaN.两种NaN之间的差异通常在Java中不可见.对信令NaN的算术运算将它们变成具有不同但通常类似的位模式的安静NaN.然而,在一些处理器上,仅复制信令NaN也执行该转换.特别地,复制信令NaN以将其返回到调用方法可以执行该转换.因此longBitsToDouble
可能无法返回具有信令NaN位模式的双精度.因此,对于某些长期值,doubleToRawLongBits(longBitsToDouble(start))
可能不相等start
.此外,哪些特定位模式表示信令NaN是平台相关的; 虽然所有NaN位模式,安静或信令,必须在上面确定的NaN范围内.
因此,假设处理double
值始终保持特定 NaN
值完整是一件危险的事情.
将干净的解决办法是存储数据的long
,并转换为double
后检查你的特殊价值.然而,这将对性能产生明显的影响.
您可以通过strictfp
在受影响的地方添加标志来逃避.这并不以任何方式保证它能够正常工作,但它(可能)会改变JVM处理浮点值的方式,并且可能只是必要的提示.但是,它仍然不便携.