我刚刚在学习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
是什么意思?
我刚刚在学习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
是什么意思?
NaN代表“不是一个数字”。
溢出的运算结果为有符号无穷大,下溢的运算结果为非规格化值或带符号零,没有数学确定结果的运算返回NaN。所有以NaN作为操作数的数值运算的结果都是NaN。正如已经描述过的那样,NaN是无序的,因此涉及一个或两个NaN的数值比较运算返回
false
,任何涉及NaN的!=
比较运算都返回true
,包括当x
为NaN时的x!=x
。
NaN的定义是不等于包括它本身在内的任何数字。这是IEEE 754标准的一部分并由CPU/FPU实现,JVM不需要添加任何逻辑来支持它。
http://en.wikipedia.org/wiki/NaN
与NaN进行比较时,即使与自身比较也始终返回无序结果。... 等式和不等式谓词是非信号的,因此x = x返回false可用于测试x是否为安静NaN。
Java将所有NaN视为安静NaN。
为什么会这样
NaN
表示“不是一个数字”。什么不是数字?任何东西都可能不是。你可以在一边放任何东西,在另一边放任何东西,因此没有保证两者相等。 NaN
通过 Double.longBitsToDouble(0x7ff8000000000000L)
计算得出,正如你可以在 longBitsToDouble
的文档中看到的:
如果参数是范围为
0x7ff0000000000001L
到0x7fffffffffffffffL
或范围为0xfff0000000000001L
到0xffffffffffffffffL
的任何值,则结果是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
:
如果
this
和argument
都代表Double.NaN
,则equals
方法返回true
,即使Double.NaN == Double.NaN
的值为false
。
Double.NaN.equals(Double.NaN);
NaN != NaN
为假会比 NaN != NaN
为真更加复杂吗?我知道IEEE已经做出了这个决定很久了,但从实际角度来看,我从未见过有用的情况。如果一个操作应该运行直到连续迭代产生相同的结果,如果没有这种行为,那么两个连续的迭代产生NaN将被“自然地”检测为退出条件。 - supercatf(f(f...f(x)))
的极限,并且找到某个n
使得y=f[n](x)
,并且f(y)
的结果与y
无法区分,则y
将与任何更深层次的f(f(f(...f(y)))
的结果无法区分。即使我们希望NaN==NaN
为false,让Nan!=Nan
也为false比让某些x的x!=x
为true更少“令人惊讶”。 - supercatDouble.NaN
的类型不是Double
,而是double
,所以这个问题与double
的行为有关。虽然存在可以测试涉及double
值的等价关系的函数,但我知道的唯一令人信服的答案是“因为IEEE的一些人认为相等性测试不应该定义一个等价关系”。顺便问一下,有没有简洁的惯用方法只使用原始运算符来测试x
和y
的等价性?我知道的所有公式都相当笨重。 - supercat这可能不是对问题的直接回答。但如果您想检查某个值是否等于Double.NaN
,您应该使用以下代码:
double d = Double.NaN
Double.isNaN(d);
这将返回 true
有趣的是,一个常量,它持有double类型的Not-a-Number(NaN)值。 它相当于通过
Double.longBitsToDouble(0x7ff8000000000000L)
返回的值。
Double
的源代码如下定义了NaN
:public static final double NaN = 0.0d / 0.0;
您所描述的特殊行为已经硬编码在JVM中。
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"
NaN是一个特殊的值,表示“不是数字”,它是某些无效算术操作的结果,例如sqrt(-1)
,并且具有(有时很烦人的)属性,即NaN != NaN
。
NaN表示操作的结果无法用数字表示。最著名的操作是0/0,其结果未知。
因此,NaN与任何东西(包括其他非数字值)都不相等。要了解更多信息,请查看维基百科页面:http://en.wikipedia.org/wiki/NaN
0/0
的结果。0/0
始终为NaN,但是其他操作的结果可能是NaN,例如2+NaN
:没有数学明确结果的操作会产生NaN
,如@AdrianMitev的答案所述。 - ANevesNaN
表示"数学上未定义",例如:"0除以0的结果未定义",因为它是未定义的,所以"与未定义相关的比较当然是未定义的"。此外,它更像是数学前提。另一方面,正无穷和负无穷都是预定义和明确的,例如"正或负无穷大在数学上是明确定义的"。如果你有变量
Double a = Double.NaN
使用
String.valueOf(Double.NaN) == a.toString()
In [1]: NaN==NaN Out[1]: False
。 - tdcDouble.NaN
是类型为java.lang.Double
的话,与Double.NaN==Double.NaN
的比较确实应该返回true。然而,它的类型是原始类型double
,因此适用于double
的运算符规则(根据IEEE 754标准要求不相等),这些规则在回答中有所解释。 - sleske