Was bedeutet NaN in Java?


106

Ich habe ein Programm, das versucht, eine doubleauf eine gewünschte Zahl zu verkleinern . Die Ausgabe, die ich bekomme, ist NaN.

Was macht NaN bedeutet in Java?


Es gibt eine gute Beschreibung von NaN und der häufigsten Fallstricke bei der Verwendung von NaN in Java: ppkwok.blogspot.co.uk/2012/11/…
Phil

Wenn Sie fragen "Was nützt NaN?" In Java (oder einer anderen Sprache) kann ich Ihnen einen Anwendungsfall geben, in dem es sehr praktisch ist: Wenn ich ein 2-D-Array von Floats habe, meine Berechnung jedoch für einen Teil dieses 2-D-Arrays keinen aussagekräftigen Wert hat, Ich werde diesen Wert mit "NaN" füllen. Dies kann verwendet werden, um nachgeschalteten Benutzern meiner Berechnung zu signalisieren (z. B. wenn sie in ein Rasterbild umgewandelt wird), "achten Sie an dieser Stelle nicht auf den Wert". Sehr hilfreich!
Dan H

Übrigens, was genau bedeutet es, ein Doppel zu "schrumpfen"? Neugierig ...
Dan H

Antworten:


152

Entnommen von dieser Seite :

"NaN" steht für "keine Zahl". "Nan" wird erzeugt, wenn eine Gleitkommaoperation einige Eingabeparameter hat, die bewirken, dass die Operation ein undefiniertes Ergebnis erzeugt. Zum Beispiel ist 0.0 geteilt durch 0.0 arithmetisch undefiniert. Die Quadratwurzel einer negativen Zahl zu ziehen ist ebenfalls undefiniert.


16
Darüber hinaus wird NaN durch den IEEE-Standard für Gleitkomma-Arithmetik (IEEE 754) ganz explizit definiert, dem Java blind folgt. Das Lesen des Standards öffnet Ihnen die Augen für viele Dinge, wobei die mehreren Werte von Null eines der Dinge sind.
Esko

37
Hat NaNauch die interessante Eigenschaft, die einzige "Zahl" zu sein, die im Vergleich nicht mit sich selbst identisch ist. Daher ist ein gemeinsame (und in vielen Sprachen der einzige) Test , ob eine Zahl xist NaNdie folgende:boolean isNaN(x){return x != x;}
quazgar

3
Link als Antwort ist tot?
Pang

3
... "Die Quadratwurzel der negativen Zahl zu ziehen ist undefiniert (in der Arithmetik)" ... ist es nicht! Es ist tatsächlich iund einige Sprachen wie Python kommen sehr gut damit zurecht ... Es mag bei javadir nicht der Fall sein
Rafael T

5
@ RafaelT Ich würde sagen, es ist undefiniert in nicht komplexer Arithmetik. In Java gibt es keine Möglichkeit, einem Float oder Double eine komplexe Zahl zuzuweisen. Python ist dynamisch typisiert, daher kann es in diesem Fall möglich sein, nur eine komplexe Zahl zurückzugeben.
Sstn

19

NaNbedeutet „keine Zahl“ und ist im Grunde eine Darstellung eines speziellen Gleitkommawertes im Gleitkomma- Standard IEE 754 . NaN bedeutet im Allgemeinen, dass der Wert nicht mit einer gültigen Gleitkommazahl ausgedrückt werden kann.

Eine Konvertierung führt zu diesem Wert, wenn der zu konvertierende Wert etwas anderes ist, z. B. wenn eine Zeichenfolge konvertiert wird, die keine Zahl darstellt.


Konvertieren wie? Mit parseFloat()oder parseDouble? Oder etwas anderes?
Alonso del Arte

14

NaNbedeutet "keine Zahl" und ist das Ergebnis undefinierter Operationen an Gleitkommazahlen, wie zum Beispiel das Teilen von Null durch Null. (Beachten Sie, dass das Teilen einer Zahl ungleich Null durch Null in der Mathematik normalerweise ebenfalls undefiniert ist, jedoch nicht zu NaN, sondern zu einer positiven oder negativen Unendlichkeit führt.)


5

NaNbedeutet "keine Zahl." Es ist ein spezieller Gleitkommawert, der bedeutet, dass das Ergebnis einer Operation nicht als reelle Zahl definiert oder nicht darstellbar war.

Sehen Sie hier für weitere Erläuterung dieses Wertes.




4

Bedeutet keine Zahl. Es ist eine übliche Darstellung für einen unmöglichen numerischen Wert in vielen Programmiersprachen.


4

Minimal lauffähiges Beispiel

Das erste, was Sie wissen müssen, ist, dass das Konzept von NaN direkt auf der CPU-Hardware implementiert ist.

Alle wichtigen modernen CPUs scheinen IEEE 754 zu folgen, das Gleitkommaformate spezifiziert, und NaNs, die nur spezielle Gleitkommawerte sind, sind Teil dieses Standards.

Daher ist das Konzept in jeder Sprache sehr ähnlich, einschließlich Java, das nur Gleitkomma-Code direkt an die CPU ausgibt.

Bevor Sie fortfahren, sollten Sie zunächst die folgenden Antworten lesen, die ich geschrieben habe:

Nun zu einer Java-Aktion. Die meisten Funktionen von Interesse, die nicht in der Kernsprache enthalten sind, leben im Inneren java.lang.Float.

Nan.java

import java.lang.Float;
import java.lang.Math;

public class Nan {
    public static void main(String[] args) {
        // Generate some NaNs.
        float nan            = Float.NaN;
        float zero_div_zero  = 0.0f / 0.0f;
        float sqrt_negative  = (float)Math.sqrt(-1.0);
        float log_negative   = (float)Math.log(-1.0);
        float inf_minus_inf  = Float.POSITIVE_INFINITY - Float.POSITIVE_INFINITY;
        float inf_times_zero = Float.POSITIVE_INFINITY * 0.0f;
        float quiet_nan1     = Float.intBitsToFloat(0x7fc00001);
        float quiet_nan2     = Float.intBitsToFloat(0x7fc00002);
        float signaling_nan1 = Float.intBitsToFloat(0x7fa00001);
        float signaling_nan2 = Float.intBitsToFloat(0x7fa00002);
        float nan_minus      = -nan;

        // Generate some infinities.
        float positive_inf   = Float.POSITIVE_INFINITY;
        float negative_inf   = Float.NEGATIVE_INFINITY;
        float one_div_zero   = 1.0f / 0.0f;
        float log_zero       = (float)Math.log(0.0);

        // Double check that they are actually NaNs.
        assert  Float.isNaN(nan);
        assert  Float.isNaN(zero_div_zero);
        assert  Float.isNaN(sqrt_negative);
        assert  Float.isNaN(inf_minus_inf);
        assert  Float.isNaN(inf_times_zero);
        assert  Float.isNaN(quiet_nan1);
        assert  Float.isNaN(quiet_nan2);
        assert  Float.isNaN(signaling_nan1);
        assert  Float.isNaN(signaling_nan2);
        assert  Float.isNaN(nan_minus);
        assert  Float.isNaN(log_negative);

        // Double check that they are infinities.
        assert  Float.isInfinite(positive_inf);
        assert  Float.isInfinite(negative_inf);
        assert !Float.isNaN(positive_inf);
        assert !Float.isNaN(negative_inf);
        assert one_div_zero == positive_inf;
        assert log_zero == negative_inf;
            // Double check infinities.

        // See what they look like.
        System.out.printf("nan            0x%08x %f\n", Float.floatToRawIntBits(nan           ), nan           );
        System.out.printf("zero_div_zero  0x%08x %f\n", Float.floatToRawIntBits(zero_div_zero ), zero_div_zero );
        System.out.printf("sqrt_negative  0x%08x %f\n", Float.floatToRawIntBits(sqrt_negative ), sqrt_negative );
        System.out.printf("log_negative   0x%08x %f\n", Float.floatToRawIntBits(log_negative  ), log_negative  );
        System.out.printf("inf_minus_inf  0x%08x %f\n", Float.floatToRawIntBits(inf_minus_inf ), inf_minus_inf );
        System.out.printf("inf_times_zero 0x%08x %f\n", Float.floatToRawIntBits(inf_times_zero), inf_times_zero);
        System.out.printf("quiet_nan1     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan1    ), quiet_nan1    );
        System.out.printf("quiet_nan2     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan2    ), quiet_nan2    );
        System.out.printf("signaling_nan1 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan1), signaling_nan1);
        System.out.printf("signaling_nan2 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan2), signaling_nan2);
        System.out.printf("nan_minus      0x%08x %f\n", Float.floatToRawIntBits(nan_minus     ), nan_minus     );
        System.out.printf("positive_inf   0x%08x %f\n", Float.floatToRawIntBits(positive_inf  ), positive_inf  );
        System.out.printf("negative_inf   0x%08x %f\n", Float.floatToRawIntBits(negative_inf  ), negative_inf  );
        System.out.printf("one_div_zero   0x%08x %f\n", Float.floatToRawIntBits(one_div_zero  ), one_div_zero  );
        System.out.printf("log_zero       0x%08x %f\n", Float.floatToRawIntBits(log_zero      ), log_zero      );

        // NaN comparisons always fail.
        // Therefore, all tests that we will do afterwards will be just isNaN.
        assert !(1.0f < nan);
        assert !(1.0f == nan);
        assert !(1.0f > nan);
        assert !(nan == nan);

        // NaN propagate through most operations.
        assert Float.isNaN(nan + 1.0f);
        assert Float.isNaN(1.0f + nan);
        assert Float.isNaN(nan + nan);
        assert Float.isNaN(nan / 1.0f);
        assert Float.isNaN(1.0f / nan);
        assert Float.isNaN((float)Math.sqrt((double)nan));
    }
}

GitHub stromaufwärts .

Laufen Sie mit:

javac Nan.java && java -ea Nan

Ausgabe:

nan            0x7fc00000 NaN
zero_div_zero  0x7fc00000 NaN
sqrt_negative  0xffc00000 NaN
log_negative   0xffc00000 NaN
inf_minus_inf  0x7fc00000 NaN
inf_times_zero 0x7fc00000 NaN
quiet_nan1     0x7fc00001 NaN
quiet_nan2     0x7fc00002 NaN
signaling_nan1 0x7fa00001 NaN
signaling_nan2 0x7fa00002 NaN
nan_minus      0xffc00000 NaN
positive_inf   0x7f800000 Infinity
negative_inf   0xff800000 -Infinity
one_div_zero   0x7f800000 Infinity
log_zero       0xff800000 -Infinity

Daraus lernen wir ein paar Dinge:

  • seltsame schwebende Operationen, die kein vernünftiges Ergebnis haben, geben NaN:

    • 0.0f / 0.0f
    • sqrt(-1.0f)
    • log(-1.0f)

    generieren a NaN.

    In C ist es tatsächlich möglich, Signale anzufordern, die bei solchen Operationen feenableexceptausgelöst werden sollen, um sie zu erkennen, aber ich glaube nicht, dass sie in Java verfügbar sind: Warum ergibt die Ganzzahldivision durch Null 1/0 einen Fehler, aber einen Gleitkomma-Wert von 1/0 gibt "Inf" zurück?

  • Seltsame Operationen, die an der Grenze von plus oder minus unendlich liegen, ergeben jedoch + - unendlich anstelle von NaN

    • 1.0f / 0.0f
    • log(0.0f)

    0.0 fällt fast in diese Kategorie, aber wahrscheinlich ist das Problem, dass es entweder auf plus oder minus unendlich gehen könnte, so dass es als NaN belassen wurde.

  • Wenn NaN der Eingang einer Floating-Operation ist, ist der Ausgang tendenziell auch NaN

  • mehrere mögliche Werte für NaN gibt es 0x7fc00000, 0x7fc00001, 0x7fc00002, obwohl x86_64 scheint nur dann zu erzeugen 0x7fc00000.

  • NaN und unendlich haben eine ähnliche binäre Darstellung.

    Lassen Sie uns einige davon aufschlüsseln:

    nan          = 0x7fc00000 = 0 11111111 10000000000000000000000
    positive_inf = 0x7f800000 = 0 11111111 00000000000000000000000
    negative_inf = 0xff800000 = 1 11111111 00000000000000000000000
                                | |        |
                                | |        mantissa
                                | exponent
                                |
                                sign

    Daraus bestätigen wir, was IEEE754 spezifiziert:

    • Sowohl NaN als auch Unendlichkeiten haben Exponenten == 255 (alle)
    • Unendlichkeiten haben Mantisse == 0. Es gibt daher nur zwei mögliche Unendlichkeiten: + und -, unterschieden durch das Vorzeichenbit
    • NaN hat Mantisse! = 0. Es gibt daher mehrere Möglichkeiten, außer Mantisse == 0, die unendlich ist
  • NaNs können entweder positiv oder negativ sein (oberes Bit), obwohl dies keinen Einfluss auf den normalen Betrieb hat

Getestet in Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.


3

Kein Java-Typ, aber in JS und anderen Sprachen verwende ich "Not a Number", was bedeutet, dass eine Operation dazu führte, dass es keine gültige Nummer wurde.


3

Es bedeutet wörtlich "keine Zahl". Ich vermute, dass etwas mit Ihrem Konvertierungsprozess nicht stimmt.

Lesen Sie den Abschnitt Not A Number unter dieser Referenz


3

Kein gültiger Gleitkommawert (z. B. das Ergebnis der Division durch Null)

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


Ich streite mit dieser Antwort. Erstens: "NaN" ist ein gültiger Wert für einen IEEE-Float! (Immerhin ist es in der Spezifikation definiert ... also ist es "gültig", oder?). Zweitens: "Division durch Null" kann durch IEEE "Positive Infinity" oder "Negative Infinity" dargestellt werden; Ein besseres Beispiel für "NaN" ist "Null geteilt durch Null", wie einige andere Antworten richtig gezeigt haben.
Dan H

"Gültiger Wert" und "in Spezifikation definiert" ist nicht dasselbe. Einverstanden mit 0/0.
Vladimir Dyuzhev
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.