In Java, cosa significa NaN?


107

Ho un programma che cerca di ridurre doublea un numero desiderato. L'output che ottengo è NaN.

Cosa NaNsignifica in Java?


C'è una buona descrizione di NaN e delle insidie ​​comuni quando si utilizza NaN in Java: ppkwok.blogspot.co.uk/2012/11/…
Phil

Se stai chiedendo "a cosa serve NaN?" in Java (o qualsiasi altro linguaggio), posso darti un caso d'uso in cui è molto utile: quando ho un array 2-D di float, ma il mio calcolo non ha un valore significativo per una parte di quell'array 2-D, Riempio quel valore con "NaN". Questo può essere utilizzato per segnalare agli utenti a valle del mio calcolo (come quando viene trasformato in un'immagine raster) "non prestare attenzione al valore a questo punto". Molto utile!
Dan H

A proposito, cosa significa esattamente "rimpicciolire" un doppio? Curioso ...
Dan H

Risposte:


153

Tratto da questa pagina :

"NaN" sta per "not a number". "Nan" viene prodotto se un'operazione in virgola mobile ha alcuni parametri di input che fanno sì che l'operazione produca un risultato indefinito. Ad esempio, 0,0 diviso per 0,0 è aritmeticamente indefinito. Anche il calcolo della radice quadrata di un numero negativo è indefinito.


16
Inoltre, NaN è definito abbastanza esplicitamente dallo standard IEEE per l'aritmetica a virgola mobile (IEEE 754) che Java segue ciecamente. Leggere lo standard ti apre gli occhi su molte cose, i molteplici valori di zero sono una delle cose.
Esko

37
Inoltre, NaNha l'interessante proprietà di essere l'unico "numero" che non è uguale a se stesso se confrontato. Quindi un comune (e in molte lingue l'unico) di prova se un numero xè NaNil seguente:boolean isNaN(x){return x != x;}
quazgar

3
Il link in risposta è morto?
Pang

3
... "La radice quadrata di un numero negativo non è definita (in aritmetica)" ... Non è così! in realtà è ie alcuni linguaggi come Python lo affrontano molto bene ... Potrebbe non essere il caso di javate
Rafael T

5
@RafaelT direi che è indefinito in aritmetica non complessa. Non c'è modo di assegnare un numero complesso a un float o double in Java. Python è tipizzato dinamicamente, quindi potrebbe essere possibile restituire solo un numero complesso in questo caso.
sstn

19

NaNsignifica "Non un numero" ed è fondamentalmente una rappresentazione di un valore in virgola mobile speciale nello standard a virgola mobile IEE 754 . NaN generalmente significa che il valore è qualcosa che non può essere espresso con un numero in virgola mobile valido.

Una conversione risulterà in questo valore, quando il valore convertito è qualcos'altro, ad esempio quando si converte una stringa che non rappresenta un numero.


Conversione come? Con parseFloat()o parseDouble? O qualcos'altro?
Alonso del Arte

14

NaNsignifica "Non un numero" ed è il risultato di operazioni indefinite su numeri in virgola mobile, come ad esempio la divisione di zero per zero. (Notare che mentre la divisione di un numero diverso da zero per zero è solitamente indefinita anche in matematica, non si traduce in NaN ma in un infinito positivo o negativo).


5

NaNsignifica "Non un numero". È un valore in virgola mobile speciale che significa che il risultato di un'operazione non è stato definito o non è rappresentabile come numero reale.

Vedi qui per ulteriori spiegazioni di questo valore.




4

Non significa un numero. È una rappresentazione comune di un valore numerico impossibile in molti linguaggi di programmazione.


4

Esempio minimo eseguibile

La prima cosa che devi sapere è che il concetto di NaN è implementato direttamente sull'hardware della CPU.

Tutte le principali CPU moderne sembrano seguire IEEE 754 che specifica i formati in virgola mobile e NaN, che sono solo valori speciali in virgola mobile, fanno parte di quello standard.

Pertanto, il concetto sarà molto simile in qualsiasi linguaggio, incluso Java che emette semplicemente codice in virgola mobile direttamente alla CPU.

Prima di procedere, potresti voler leggere prima le seguenti risposte che ho scritto:

Ora per alcune azioni Java. La maggior parte delle funzioni di interesse che non sono nella lingua principale risiedono all'interno 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 a monte .

Corri con:

javac Nan.java && java -ea Nan

Produzione:

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

Quindi da questo impariamo alcune cose:

  • strane operazioni fluttuanti che non hanno alcun risultato sensato danno NaN:

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

    generare un file NaN.

    In C, è effettivamente possibile richiedere che vengano generati segnali su tali operazioni feenableexceptper rilevarli, ma non credo che sia esposto in Java: Perché la divisione intera per zero 1/0 dà errore ma virgola mobile 1 / 0.0 restituisce "Inf"?

  • operazioni strane che sono sul limite di più o meno infinito tuttavia danno + - infinito invece di NaN

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

    0.0 rientra quasi in questa categoria, ma probabilmente il problema è che potrebbe andare a più o meno infinito, quindi è stato lasciato come NaN.

  • se NaN è l'input di un'operazione floating, anche l'output tende ad essere NaN

  • ci sono vari valori possibili per NaN 0x7fc00000, 0x7fc00001, 0x7fc00002, anche se x86_64 sembra generare solo 0x7fc00000.

  • NaN e infinity hanno una rappresentazione binaria simile.

    Analizziamo alcuni di loro:

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

    Da questo confermiamo quanto specifica IEEE754:

    • sia NaN che infiniti hanno esponente == 255 (tutti)
    • gli infiniti hanno mantissa == 0. Ci sono quindi solo due possibili infiniti: + e -, differenziati dal bit di segno
    • NaN ha mantissa! = 0. Ci sono quindi diverse possibilità, eccetto mantissa == 0 che è infinito
  • I NaN possono essere positivi o negativi (bit superiore), sebbene ciò non abbia effetto sulle normali operazioni

Testato in Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.


3

Non è un tipo Java, ma in JS e in altri linguaggi uso è "Not a Number", il che significa che alcune operazioni hanno fatto sì che diventasse un numero non valido.


3

Letteralmente significa "Non un numero". Sospetto che ci sia qualcosa di sbagliato nel tuo processo di conversione.

Controlla la sezione Non un numero a questo riferimento


3

Non è un valore in virgola mobile valido (ad esempio il risultato della divisione per zero)

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


Cavillo con questa risposta. Primo: "NaN" È un valore valido per un float IEEE! (Dopo tutto, è definito nelle specifiche ... quindi è "valido", giusto?). Secondo: la "divisione per zero" può essere rappresentata da IEEE "Positive Infinity" o "Negative Infinity"; un esempio migliore di "NaN" è "zero diviso zero", come alcune altre risposte hanno correttamente sottolineato.
Dan H

"Valore valido" e "definito nelle specifiche" non sono la stessa cosa. Concordato con 0/0.
Vladimir Dyuzhev
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.