5

我正在编写与 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)
4

1 回答 1

6

您在这里涉足非常危险的水域,这是少数没有明确指定 Java VM 行为的领域之一。

根据 JVM 规范,double范围内只有“一个 NaN 值”。双精度数的算术运算无法区分两个不同的NaN值。

的文档longBitsToDouble()有这个注释:

请注意,此方法可能无法返回double与 long 参数具有完全相同位模式的 NaN。IEEE 754 区分了两种 NaN,静默 NaN 和信令 NaN。这两种 NaN 之间的差异在 Java 中通常是不可见的。信号 NaN 的算术运算将它们变成安静的 NaN,具有不同但通常相似的位模式。但是,在某些处理器上,仅复制信号 NaN 也会执行该转换。特别是,复制一个信令 NaN 以将其返回给调用方法可以执行此转换。因此longBitsToDouble可能无法返回带有信号 NaN 位模式的双精度数。因此,对于某些 long 值,doubleToRawLongBits(longBitsToDouble(start))可能不等于start. 此外,哪些特定的位模式代表信令 NaN 取决于平台;尽管所有 NaN 位模式,安静或信令,都必须在上面确定的 NaN 范围内。

因此,假设处理一个double值将始终保持特定 NaN值不变是一件危险的事情。

干净的解决方案是存储您的数据并在检查您的特殊值long转换为。然而,这将产生相当显着的性能影响。double

可能会通过在受影响的地方添加strictfp标志来逃脱。这并不能保证它会起作用,但它会(可能)改变你的 JVM 处理浮点值的方式,并且可能只是提供帮助的必要提示。但是,它仍然不是便携式的。

于 2011-06-16T12:52:06.817 回答