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


155

我刚刚研究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意思


8
这真的很奇怪。由于Double.NaN是静态final,因此与==的比较应返回true。+1问题。
Stephan'1

2
在python中也是如此:In [1]: NaN==NaN Out[1]: False
tdc 2012年

58
在正确遵循IEEE 754标准的所有语言中也是如此。
zzzzBov 2012年

4
直觉:“ Hello”不是数字,true(布尔值)也不是数字。NaN!= NaN出于相同的原因“ Hello”!= true
Kevin

3
@Stephan:Double.NaN==Double.NaN如果Double.NaN类型为,则与的比较确实应该返回true java.lang.Double。但是,它的类型是基本类型double,并且double适用运算符规则(如答案中所述,这需要不等式才能符合IEEE 754)。
sleske 2012年

Answers:


139

NaN的意思是“不是数字”。

Java语言规范(JLS)第三版说

溢出的操作将产生有符号的无穷大,下溢的操作将产生非规范化的值或有符号的零,而没有数学上确定的结果的操作将产生NaN。将NaN作为操作数的所有数值运算都会产生NaN。如前所述,NaN是无序的,因此涉及一个或两个NaN返回的数值比较操作false以及!=涉及NaN返回的任何比较true,包括x!=x何时x是NaN。


4
@nibot:基本上是真的。与符合IEEE标准的浮点数进行任何比较都会产生false。因此,该标准与Java的不同之处在于IEEE要求(NAN != NAN) == false
德鲁·多曼

2
打开这个Pandora的盒子-您在哪里看到“ IEEE要求(NAN!= NAN)==假”?
主管

62

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

http://en.wikipedia.org/wiki/NaN

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

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


1
它是由CPU实现的,还是像Bohemian提到的那样在JVM中进行了硬连接?
Naweed Chougle

3
JVM必须调用将正确实现它的任何东西。在PC上,CPU照此完成所有工作。在没有此支持的机器上,JVM必须实现它。(我不知道有这样的机器)
彼得·劳瑞

可以选择8087的那个年代,C库包含一个FP仿真器。像JVM这样的程序都不必担心这两种方式。
罗恩侯爵

49

为什么这样的逻辑

NaN意味着Not a Number。什么不是数字?什么都可以 您可以在一侧拥有任何东西,而在另一侧拥有任何东西,因此无法保证两者相等。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

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

Double.NaN.equals(Double.NaN);

您是否知道在任何情况下NaN != NaN为假都会使程序比NaN != NaN为真复杂吗?我知道IEEE是在很久以前就做出决定的,但是从实际的角度来看,我从未见过有用的决定。如果应该运行某个操作,直到连续的迭代产生相同的结果,则如果不针对该行为,将“自然地”检测到两次自然连续的NaN作为退出条件。
超级猫

@supercat你怎么能说两个随机非数字自然相等?还是说,本来就相等?将NaN视为实例,而不是原始的东西。每个不同的异常结果都是某种奇怪事物的不同实例,即使两者都应表示相同,对于不同的实例使用==必须返回false。另一方面,当使用equals时,可以按预期正确处理。[ docs.oracle.com/javase/7/docs/api/java/lang/...
falsarella

@falsarella:问题不在于两个随机数是否应被视为“绝对相等”,而是在什么情况下将任何数字与自身“绝对不相等”进行比较很有用。如果一个人试图计算的极限f(f(f...f(x))),而一个y=f[n](x)人为求一个n使得的结果与f(y)不能区分y,那么y它将与任何更深层的嵌套的结果无法区分f(f(f(...f(y)))。即使一个人想要NaN==NaN是假的,Nan!=Nan 也要假的也不会比x!=x某个x的正确要“令人惊讶” 。
2013年

1
@falsarella:我相信Double.NaN不是的类型Double,但是double,所以问题是与行为有关的double。尽管存在可以测试涉及double值的等价关系的函数,但是我所知道的关于“为什么”(这是原始问题的一部分)的唯一令人信服的答案是“因为IEEE的某些人不认为平等测试应该定义等价关系”。顺便说一句,是否有任何简明的惯用方式来测试xy仅使用原始运算符的等效性?我所知道的所有公式都比较笨拙。
2013年

1
最佳和简单的答案。谢谢
Tarun Nagpal

16

这可能不是该问题的直接答案。但是,如果要检查是否等于某个值,Double.NaN则应使用以下命令:

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

这将返回 true


6

Double.NaNJavadoc可以说明一切:

持有类型为Not-a-Number(NaN)的常数double。它等于所返回的值Double.longBitsToDouble(0x7ff8000000000000L)

有趣的是,用于Double定义的来源NaN因此:

public static final double NaN = 0.0d / 0.0;

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


5
是在JVM中进行硬连线,还是像Peter所提到的那样由CPU实现?
Naweed Chougle

4

按照,用于浮点运算的IEEE标准用于双精度数字的,

IEEE双精度浮点标准表示需要一个64位字,该字可以表示为从0到63的编号,从左到右

在此处输入图片说明 哪里,

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

如果E=2047(全部E1)并且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"

3

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


2

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

因此,NaN不等于任何值(包括其他非数字值)。有关更多信息,只需检查Wikipedia页面:http : //en.wikipedia.org/wiki/NaN


-1:它不能代表结果0/00/0始终是NaN,但NaN可以是其他运算的结果-例如2+NaN::an operation that has no mathematically definite result produces NaN@AdrianMitev的回答
ANeves 2012年

的确,NaN代表“不是数字”,它是所有操作的结果,这些操作具有不确定的值或无法表示的值。最著名和最常见的运算是0/0,但显然还有其他大量运算具有相同的结果。我同意我的答案可以改善,但我不同意-1。。。我只是检查了维基百科也将0/0运算作为具有NaN结果的第一个运算示例(en.wikipedia.org/wiki/ NaN)。
Matteo

而且,这在Double的Java源代码中:public static final double NaN = 0.0d / 0.0;
纪尧姆

1
@Matteo +0,现在错误的陈述消失了。而我的-1或+1则不代表您同意或不同意;但最好用-1留下评论,以便作者可以理解为什么他的答案被认为没有用-如果愿意,可以更改答案。
ANeves 2012年

@Guillaume如果该评论对我有用,请改一下:我不明白。
ANeves 2012年

0

根据此链接,它有多种情况,难以记住。这就是我记住并区分它们的方式。NaN意思是“数学上未定义”,例如:“ 0除以0的结果是未定义的”,并且因为它是未定义的,所以“与未定义的比较当然是未定义的”。此外,它的工作方式更像数学前提。另一方面,正和负无穷都是预定义和确定的,例如“正或负无穷大在数学上已经很好地定义了”。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.