编程语言
首页 > 编程语言> > java – 运行时期间最终的非规范NaN双值更改

java – 运行时期间最终的非规范NaN双值更改

作者:互联网

我正在编写与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,但很难分辨,因为将它打印到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)

测试有时会失败:

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规范,双范围内只有“NaN值”.对双精度数没有算术运算可以区分两个不同的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. 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.

因此,假设处理double值将始终保持特定的NaN值完整是一件危险的事情.

最干净的解决方案是将数据存储为long,并在检查特殊值后转换为double.然而,这将对性能产生明显的影响.

您可以通过在受影响的地方添加strictfp标志来逃避.这并不以任何方式保证它能够正常工作,但它(可能)会改变JVM处理浮点值的方式,并且可能只是必要的提示.但是,它仍然不便携.

标签:java,jvm-crash
来源: https://codeday.me/bug/20190518/1127874.html