为什么 Double.NaN = = Double.NaN 返回 false?

我正在研究 OCPJP 的问题,我发现了这个奇怪的代码:

public static void main(String a[]) {
System.out.println(Double.NaN==Double.NaN);
System.out.println(Double.NaN!=Double.NaN);
}

当我运行代码时,我得到:

false
true

当我们比较两个看起来相同的东西时,输出 false如何?NaN是什么意思?

69233 次浏览

NaN 是一个特殊的值,表示“非数字”; 它是某些无效算术操作(如 sqrt(-1))的结果,并具有 NaN != NaN的属性(有时很烦人)。

Javadoc for Double.NaN说明了一切:

保存 double类型的 Not-a-Number (NaN)值的常数。它等效于 Double.longBitsToDouble(0x7ff8000000000000L)返回的值。

有趣的是,Double的来源是这样定义 NaN的:

public static final double NaN = 0.0d / 0.0;

您所描述的特殊行为是硬连接到 JVM 中的。

没有一个数字表示其结果不能用数字表示的操作的结果。最著名的运算是0/0,其结果是未知的。

因此,NaN 不等于任何值(包括其他非 a 值)。想了解更多信息,请查看维基百科页面: http://en.wikipedia.org/wiki/NaN

为什么这么说

NaN代表 Not a Number。什么不是数字?什么都行。你可以在一边有任何东西,在另一边有任何东西,所以没有什么能保证两者是平等的。NaN是用 Double.longBitsToDouble(0x7ff8000000000000L)计算的,正如你在 longBitsToDouble的文档中看到的:

如果参数是范围为 0x7ff0000000000001L0x7fffffffffffffffL或在范围 0xfff0000000000001L至 结果是 NaN

此外,NaN在 API 内部进行逻辑处理。


文件

/**
* A constant holding a Not-a-Number (NaN) value of type
* {@code double}. It is equivalent to the value returned by
* {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
*/
public static final double NaN = 0.0d / 0.0;

顺便说一下,NaN 作为您的代码示例进行了测试:

/**
* Returns {@code true} if the specified number is a
* Not-a-Number (NaN) value, {@code false} otherwise.
*
* @param   v   the value to be tested.
* @return  {@code true} if the value of the argument is NaN;
*          {@code false} otherwise.
*/
static public boolean isNaN(double v) {
return (v != v);
}

解决方案

你可以使用 compare/compareTo:

这种方法认为 Double.NaN等于它本身 和大于所有其他 double值(包括 Double.POSITIVE_INFINITY).

Double.compare(Double.NaN, Double.NaN);
Double.NaN.compareTo(Double.NaN);

或者,equals:

如果 thisargument都表示 Double.NaN,则 equals方法返回 true,即使 Double.NaN==Double.NaN的值为 false

Double.NaN.equals(Double.NaN);

根据定义,NaN 不等于包括 NaN 在内的任何数字。这是 IEEE 754标准的一部分,由 CPU/FPU 实现。它不是 JVM 必须添加任何逻辑才能支持的东西。

Http://en.wikipedia.org/wiki/nan

与 NaN 的比较始终返回无序的结果,即使与它自身进行比较也是如此。... 相等和不等谓词是无信号的,所以 x = x 返回 false 可以用来测试 x 是否是一个安静的 NaN。

Java 将所有 NaN 视为安静的 NaN。

按照双精度号码的 浮点运算的 IEEE 标准,

IEEE 双精度浮点标准表示 需要一个64位的单词,这个单词可以用0到0之间的数字表示 63从左到右

enter image description here 在哪里,

S: Sign – 1 bit
E: Exponent – 11 bits
F: Fraction – 52 bits

如果 E=2047(所有的 E都是 1)和 F是非零的,那么 V=NaN(“非数字”)

也就是说,

如果所有的 E位都是1,如果在 F中有任何非零位,那么数字就是 NaN

因此,除其他外,以下所有数字都是 NaN,

0 11111111 0000000000000000010000000000000000000000000000000000 = NaN
1 11111111 0000010000000000010001000000000000001000000000000000 = NaN
1 11111111 0000010000011000010001000000000000001000000000000000 = NaN

特别是,您不能测试

if (x == Double.NaN)

检查特定结果是否等于 Double.NaN,因为所有“非数字”值都被认为是不同的。不过,你可以使用 Double.isNaN方法:

if (Double.isNaN(x)) // check whether x is "not a number"

这可能不是这个问题的直接答案。 但是如果你想检查某些东西是否等于 Double.NaN,你应该这样做:

double d = Double.NaN
Double.isNaN(d);

这将返回 true

根据这个 链接,它有各种各样的情况和难以记住。这就是我记忆和区分他们的方式。NaN的意思是“数学上未定义的”,例如: “0除以0的结果是未定义的”,因为它是未定义的,所以“与未定义的相关的比较当然是未定义的”。而且,它更像是一个数学前提。另一方面,正无穷大和负无穷大都是预定义的和确定的,例如“正无穷大或负无穷大都是精确定义的”。

如果你有变量

Double a = Double.NaN

使用

String.valueOf(Double.NaN) == a.toString()