Warum gibt Double.NaN == Double.NaN false zurück?


155

Ich habe gerade OCPJP-Fragen studiert und diesen seltsamen Code gefunden:

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

Als ich den Code ausführte, bekam ich:

false
true

Wie ist die Ausgabe, falsewenn wir zwei Dinge vergleichen, die gleich aussehen? Was heißt NaNdas


8
Das ist wirklich komisch. Da Double.NaN statisch final ist, sollte der Vergleich mit == true zurückgeben. +1 für die Frage.
Stephan

2
Das gleiche gilt für Python:In [1]: NaN==NaN Out[1]: False
tdc

58
Gleiches gilt für alle Sprachen, die dem IEEE 754-Standard korrekt entsprechen.
zzzzBov

4
Intuition: "Hallo" ist keine Zahl, true (boolean) ist auch keine Zahl. NaN! = NaN aus dem gleichen Grund "Hallo"! = True
Kevin

3
@Stephan: Der Vergleich mit Double.NaN==Double.NaNsollte tatsächlich true zurückgeben, wenn er Double.NaNvom Typ wäre java.lang.Double. Sein Typ ist jedoch das Grundelement double, und es gelten die Operatorregeln double(die diese Ungleichung für die Konformität mit IEEE 754 erfordern, wie in den Antworten erläutert).
Sleske

Antworten:


139

NaN bedeutet "keine Zahl".

In der dritten Ausgabe der Java Language Specification (JLS) heißt es :

Eine Operation, die überläuft, erzeugt eine vorzeichenbehaftete Unendlichkeit, eine Operation, die unterläuft, erzeugt einen denormalisierten Wert oder eine vorzeichenbehaftete Null, und eine Operation, die kein mathematisch bestimmtes Ergebnis hat, erzeugt NaN. Alle numerischen Operationen mit NaN als Operanden erzeugen als Ergebnis NaN. Wie bereits beschrieben wurde, ist NaN ungeordnet, so dass eine numerische Vergleichsoperation mit ein oder zwei NaN-Rückgaben falseund jeder !=Vergleich mit NaN-Rückgaben true, einschließlich x!=xwann xNaN ist.


4
@nibot: Meistens wahr . Jeder Vergleich mit einem IEEE-konformen Schwimmer ergibt false. Dieser Standard unterscheidet sich von Java darin, dass IEEE dies verlangt (NAN != NAN) == false.
Drew Dormann

2
Öffnen Sie die Büchse dieser Pandora - wo sehen Sie, dass "IEEE das verlangt (NAN! = NAN) == false"?
Supervisor

62

NaN ist per Definition nicht gleich einer Zahl einschließlich NaN. Dies ist Teil des IEEE 754-Standards und wird von der CPU / FPU implementiert. Die JVM muss keine Logik zur Unterstützung hinzufügen.

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

Ein Vergleich mit einem NaN liefert auch beim Vergleich mit sich selbst immer ein ungeordnetes Ergebnis. ... Die Prädikate für Gleichheit und Ungleichheit sind nicht signalisierend, sodass x = x, das false zurückgibt, verwendet werden kann, um zu testen, ob x ein ruhiges NaN ist.

Java behandelt alles NaN als leises NaN.


1
Wird es von der CPU implementiert oder ist es in der JVM fest verdrahtet, wie Bohemian erwähnt?
Naweed Chougle

3
Die JVM muss alles aufrufen, was sie korrekt implementiert. Auf einem PC erledigt die CPU die gesamte Arbeit als solche. Auf einem Computer ohne diese Unterstützung muss die JVM diese implementieren. (Ich kenne keine solche Maschine)
Peter Lawrey

Damals, als der 8087 eine Option war, enthielt die C-Bibliothek einen FP-Emulator. Programme wie die JVM müssten sich in keiner Weise darum kümmern.
Marquis von Lorne

49

Warum diese Logik?

NaNbedeutet Not a Number. Was ist keine Nummer? Etwas. Sie können alles auf der einen Seite und alles auf der anderen Seite haben, also garantiert nichts, dass beide gleich sind. NaNwird berechnet mit Double.longBitsToDouble(0x7ff8000000000000L)und wie Sie in der Dokumentation von sehen können longBitsToDouble:

Wenn das Argument ein Wert im Bereich 0x7ff0000000000001Ldurch 0x7fffffffffffffffLoder im Bereich 0xfff0000000000001Ldurch ist 0xffffffffffffffffL, ist das Ergebnis a NaN.

Wird auch NaNinnerhalb der API logisch behandelt.


Dokumentation

/** 
 * 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;

By the way, NaN wird als Ihr Codebeispiel getestet:

/**
 * 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);
}

Lösung

Was Sie tun können, ist compare/ compareTo:

Double.NaNwird von dieser Methode als gleich und größer als alle anderen doubleWerte (einschließlich Double.POSITIVE_INFINITY) angesehen.

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

Oder equals:

Wenn thisund argumentbeide darstellen Double.NaN, gibt die equalsMethode zurück true, obwohl Double.NaN==Double.NaNsie den Wert hat false.

Double.NaN.equals(Double.NaN);

Kennen Sie einen Fall, NaN != NaNin dem Falschheit Programme komplizierter machen würde NaN != NaNals Wahrhaftigkeit? Ich weiß, dass IEEE die Entscheidung vor langer Zeit getroffen hat, aber aus praktischer Sicht habe ich noch nie Fälle gesehen, in denen dies nützlich ist. Wenn eine Operation ausgeführt werden soll, bis aufeinanderfolgende Iterationen das gleiche Ergebnis liefern, würden zwei aufeinanderfolgende Iterationen, die NaN ergeben, "natürlich" als Ausgangsbedingung erkannt, wenn dieses Verhalten nicht vorhanden wäre.
Supercat

@supercat Wie kann man sagen, dass zwei zufällige Nichtzahlen natürlich gleich sind? Oder primitiv gleich sagen? Stellen Sie sich NaN als eine Instanz vor, nicht als etwas Primitives. Jedes abnormale Ergebnis ist eine andere Instanz von etwas Seltsamem, und selbst wenn beide dasselbe darstellen sollten, muss die Verwendung von == für verschiedene Instanzen false zurückgeben. Auf der anderen Seite kann es bei Verwendung von equals richtig gehandhabt werden, wie Sie es beabsichtigen. [ docs.oracle.com/javase/7/docs/api/java/lang/…
Falsarella

@falsarella: Es geht nicht darum, ob zwei Zufallszahlen als "definitiv gleich" betrachtet werden sollen, sondern in welchen Fällen es sinnvoll ist, eine Zahl als "definitiv ungleich" mit sich selbst vergleichen zu lassen. Wenn man versucht, die Grenze von zu berechnen f(f(f...f(x))), und man y=f[n](x)für einige eine nsolche findet, von der das Ergebnis von f(y)nicht zu unterscheiden ist y, dann yist man vom Ergebnis eines tiefer verschachtelten nicht zu unterscheiden f(f(f(...f(y))). Selbst wenn man NaN==NaNfalsch sein wollte , wäre es weniger "überraschend" , Nan!=Nan auch falsch zu sein, x!=xals für einige x wahr zu sein.
Supercat

1
@falsarella: Ich glaube, die Art von Double.NaNist nicht Double, aber doubledie Frage bezieht sich also auf das Verhalten von double. Obwohl es Funktionen gibt, die auf eine Äquivalenzbeziehung mit doubleWerten testen können, ist die einzige überzeugende Antwort, die ich für "warum" (was Teil der ursprünglichen Frage ist) kenne, "weil einige Leute am IEEE nicht dachten, dass Gleichheitstests dies tun sollten eine Äquivalenzbeziehung definieren ". Übrigens, gibt es eine prägnante idiomatische Methode zum Testen xund yzur Äquivalenz, bei der nur primitive Operatoren verwendet werden? Alle Formulierungen, die ich kenne, sind ziemlich klobig.
Supercat

1
beste und einfache Antwort. Vielen Dank
Tarun Nagpal

16

Es ist möglicherweise keine direkte Antwort auf die Frage. Wenn Sie jedoch überprüfen möchten, ob etwas gleich ist, Double.NaNsollten Sie Folgendes verwenden:

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

Dies wird zurückkehren true


6

Der Javadoc für Double.NaN sagt alles:

Eine Konstante, die einen NaN-Wert (Not-a-Number) vom Typ enthält double. Dies entspricht dem von zurückgegebenen Wert Double.longBitsToDouble(0x7ff8000000000000L).

Interessanterweise Doubledefiniert die Quelle für NaN:

public static final double NaN = 0.0d / 0.0;

Das von Ihnen beschriebene spezielle Verhalten ist fest mit der JVM verbunden.


5
Ist es in der JVM fest verdrahtet oder wird es von der CPU implementiert, wie Peter erwähnt?
Naweed Chougle

4

gemäß IEEE-Standard für Gleitkomma-Arithmetik für doppelte Präzisionszahlen,

Die IEEE-Gleitkomma-Standarddarstellung mit doppelter Genauigkeit erfordert ein 64-Bit-Wort, das von links nach rechts als von 0 bis 63 nummeriert dargestellt werden kann

Geben Sie hier die Bildbeschreibung ein wo,

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

Wenn E=2047(alle Esind 1) und Fungleich Null ist, dann V=NaN("Keine Zahl")

Was bedeutet,

Wenn alle EBits 1 sind und ein Bit ungleich Null vorhanden ist, lautet Fdie Zahl NaN.

daher sind unter anderem alle folgenden Zahlen NaN:

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

Insbesondere können Sie nicht testen

if (x == Double.NaN) 

um zu überprüfen, ob ein bestimmtes Ergebnis gleich ist Double.NaN, da alle Werte "keine Zahl" als unterschiedlich betrachtet werden. Sie können jedoch die folgende Double.isNaNMethode verwenden:

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

3

NaN ist ein spezieller Wert, der "keine Zahl" bezeichnet. Es ist das Ergebnis bestimmter ungültiger arithmetischer Operationen, wie z. B. sqrt(-1)und hat die (manchmal ärgerliche) Eigenschaft, dass NaN != NaN.


2

Keine Zahl repräsentiert das Ergebnis von Operationen, deren Ergebnis mit einer Zahl nicht darstellbar ist. Die bekannteste Operation ist 0/0, deren Ergebnis nicht bekannt ist.

Aus diesem Grund ist NaN gleich nichts (einschließlich anderer Nicht-Zahlen-Werte). Weitere Informationen finden Sie auf der Wikipedia-Seite: http://en.wikipedia.org/wiki/NaN


-1: Es repräsentiert nicht das Ergebnis von 0/0. 0/0ist immer NaN, aber NaN kann das Ergebnis anderer Operationen sein - wie zum Beispiel 2+NaN: an operation that has no mathematically definite result produces NaNgemäß der Antwort von @AdrianMitev
ANeves

In der Tat steht NaN für "Not a Number" und ist das Ergebnis aller Operationen, die einen undefinierten oder nicht darstellbaren Wert haben. Die bekannteste und häufigste Operation ist 0/0, aber offensichtlich gibt es Tonnen anderer Operationen, die das gleiche Ergebnis erzielen. Ich bin damit einverstanden, dass meine Antwort verbessert werden könnte, aber ich bin nicht einverstanden mit -1 ... Ich habe gerade überprüft, dass auch Wikipedia die 0/0-Operationen als erstes Beispiel für eine Operation mit einem NaN-Ergebnis verwendet ( en.wikipedia.org/wiki/). NaN ).
Matteo

Dies ist auch in der Java-Quelle für Double: public static final double NaN = 0.0d / 0.0;
Guillaume

1
@Matteo +0, jetzt wo die falsche Aussage weg ist. Und meine -1 oder +1 sind nicht für Sie zuzustimmen oder nicht zuzustimmen; Es ist jedoch gut, einen Kommentar mit -1 zu hinterlassen, damit der Autor verstehen kann, warum seine Antwort als nutzlos angesehen wird - und sie zu ändern, wenn er dies wünscht.
Aneves

@ Guillaume Wenn dieser Kommentar für mich bestimmt war, formulieren Sie ihn bitte um: Ich verstehe ihn nicht.
Aneves

0

Nach diesem Link hat es verschiedene Situationen und schwer zu merken. So erinnere ich mich und unterscheide sie. NaNbedeutet zum Beispiel "mathematisch undefiniert": "Das Ergebnis von 0 geteilt durch 0 ist undefiniert" und weil es undefiniert ist, ist "Vergleich in Bezug auf undefiniert natürlich undefiniert". Außerdem funktioniert es eher wie mathematische Prämissen. Andererseits ist sowohl das positive als auch das negative Unendliche vordefiniert und endgültig, zum Beispiel "positiv oder negativ unendlich groß ist mathematisch gut definiert".

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.