Perché Double.NaN == Double.NaN restituisce false?


155

Stavo solo studiando le domande OCPJP e ho trovato questo strano codice:

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

Quando ho eseguito il codice, ho ottenuto:

false
true

Come è l'output falsequando stiamo confrontando due cose che sembrano uguali tra loro? Cosa NaNsignifica?


8
Questo è davvero strano. Poiché Double.NaN è statico finale, il confronto con == dovrebbe restituire true. +1 per la domanda.
Stephan,

2
Lo stesso vale per Python:In [1]: NaN==NaN Out[1]: False
tdc,

58
Lo stesso vale in tutte le lingue che seguono correttamente lo standard IEEE 754.
zzzzBov,

4
Intuizione: "Ciao" non è un numero, vero (booleano) non è un numero. NaN! = NaN per lo stesso motivo "Hello"! = True
Kevin

3
@Stephan: Il confronto con Double.NaN==Double.NaNdovrebbe effettivamente tornare vero se Double.NaNfosse di tipo java.lang.Double. Tuttavia, il suo tipo è la primitiva doublee si doubleapplicano le regole dell'operatore (che richiedono questa disuguaglianza per la conformità con IEEE 754, come spiegato nelle risposte).
sleske,

Risposte:


139

NaN significa "Not a Number".

La terza edizione di Java Language Specification (JLS) dice :

Un'operazione che trabocca produce un infinito con segno, un'operazione che trabocca produce un valore denormalizzato o uno zero con segno e un'operazione che non ha un risultato matematicamente definito produce NaN. Tutte le operazioni numeriche con NaN come operando producono di conseguenza NaN. Come è già stato descritto, NaN non è ordinata, quindi un'operazione di confronto numerico che coinvolge uno o due ritorni NaN falsee qualsiasi !=confronto che coinvolge ritorni NaN true, incluso x!=xquando xè NaN.


4
@nibot: per lo più vero . Qualsiasi confronto con un galleggiante conforme IEEE produrrà false. Quindi quello standard differisce da Java in quanto richiesto dall'IEEE (NAN != NAN) == false.
Ha disegnato Dormann il

2
Aprendo questo vaso di Pandora - dove vedi che "IEEE lo richiede (NAN! = NAN) == false"?
Supervisore

62

NaN non è per definizione uguale a nessun numero incluso NaN. Questo fa parte dello standard IEEE 754 e implementato dalla CPU / FPU. Non è qualcosa che la JVM debba aggiungere qualsiasi logica per supportare.

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

Un confronto con una NaN restituisce sempre un risultato non ordinato anche quando si confronta con se stesso. ... I predicati di uguaglianza e disuguaglianza non segnalano, quindi x = x return false può essere usato per verificare se x è un NaN silenzioso.

Java considera tutto NaN come NaN silenzioso.


1
È implementato dalla CPU o è cablato nella JVM come menziona Bohemian?
Naweed Chougle,

3
La JVM deve chiamare tutto ciò che lo implementerà correttamente. Su un PC, la CPU svolge tutto il lavoro in quanto tale. Su una macchina senza questo supporto, la JVM deve implementarla. (Non conosco nessuna di queste macchine)
Peter Lawrey, l'

Ai tempi in cui l'8087 era un'opzione, la libreria C conteneva un emulatore FP. Programmi come JVM non dovrebbero preoccuparsene in entrambi i casi.
Marchese di Lorne,

49

Perché quella logica

NaNsignifica Not a Number. Cosa non è un numero? Nulla. Puoi avere qualsiasi cosa da una parte e qualsiasi cosa dall'altra parte, quindi nulla garantisce che entrambi siano uguali. NaNviene calcolato con Double.longBitsToDouble(0x7ff8000000000000L)e come puoi vedere nella documentazione di longBitsToDouble:

Se l'argomento è un valore nell'intervallo 0x7ff0000000000001Lattraverso 0x7fffffffffffffffLo nell'intervallo 0xfff0000000000001Lattraverso 0xffffffffffffffffL, il risultato è a NaN.

Inoltre, NaNviene logicamente trattato all'interno dell'API.


Documentazione

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

A proposito, NaN viene testato come esempio di codice:

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

Soluzione

Quello che puoi fare è usare compare/ compareTo:

Double.NaNè considerato da questo metodo uguale a se stesso e maggiore di tutti gli altri doublevalori (incluso Double.POSITIVE_INFINITY).

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

Oppure equals:

Se thised argumententrambi rappresentano Double.NaN, quindi il equalsmetodo ritorna true, anche se Double.NaN==Double.NaNha il valore false.

Double.NaN.equals(Double.NaN);

Conosci qualche caso in cui NaN != NaNessere falsi renderebbe i programmi più complicati che NaN != NaNessere veri? So che IEEE ha preso la decisione anni fa, ma da un punto di vista pratico, non ho mai visto casi in cui è utile. Se si suppone che un'operazione venga eseguita fino a quando le iterazioni consecutive producono lo stesso risultato, se due iterazioni consecutive producono NaN verrebbe "naturalmente" rilevata come condizione di uscita se non fosse per quel comportamento.
Supercat,

@supercat Come puoi dire che due non numeri casuali sono naturalmente uguali? O dire, primitivamente uguale? Pensa a NaN come a un'istanza, non a qualcosa di primitivo. Ogni diverso risultato anomalo è un'istanza diversa di qualcosa di strano e anche se entrambi devono rappresentare lo stesso, usare == per istanze diverse deve restituire false. D'altra parte, quando si utilizza uguale è possibile gestirlo correttamente come desiderato. [ docs.oracle.com/javase/7/docs/api/java/lang/…
falsarella

@falsarella: Il problema non è se due numeri casuali debbano essere considerati "decisamente uguali", ma piuttosto in quali casi è utile avere un numero qualsiasi paragonabile a "decisamente diseguale" con se stesso. Se si sta tentando di calcolare il limite di f(f(f...f(x))), e si trova una y=f[n](x)per alcuni ntale da cui il risultato di f(y)è indistinguibile y, allora ysarà indistinguibile dal risultato di qualsiasi altro annidato più profondamente f(f(f(...f(y))). Anche se uno volesse NaN==NaNessere falso, Nan!=Nan anche essere falso sarebbe meno "sorprendente" che x!=xessere vero per alcuni x.
Supercat,

1
@Falsarella: credo che il tipo di Double.NaNnon lo sia Double, ma double, quindi, la domanda è una relativa al comportamento di double. Sebbene esistano funzioni che possono verificare una relazione di equivalenza che coinvolge doublevalori, l'unica risposta convincente che conosco per "perché" (che fa parte della domanda originale) è "perché alcune persone dell'IEEE non pensavano che i test di uguaglianza avrebbero dovuto definire una relazione di equivalenza ". A proposito, c'è un modo conciso idiomatico per testare xe yper l'equivalenza usando solo operatori primitivi? Tutte le formulazioni che conosco sono piuttosto goffe.
supercat

1
risposta migliore e semplice. Grazie
Tarun Nagpal,

16

Potrebbe non essere una risposta diretta alla domanda. Ma se vuoi verificare se qualcosa è uguale a Double.NaNte dovresti usare questo:

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

Questo tornerà true


6

Il javadoc per Double.NaN dice tutto:

Una costante che contiene un valore Not-a-Number (NaN) di tipo double. È equivalente al valore restituito da Double.longBitsToDouble(0x7ff8000000000000L).

È interessante notare che la fonte per Doubledefinisce NaNcosì:

public static final double NaN = 0.0d / 0.0;

Il comportamento speciale che descrivi è cablato nella JVM.


5
È cablato nella JVM o è implementato dalla CPU come menziona Peter?
Naweed Chougle,

4

secondo, lo standard IEEE per l'aritmetica in virgola mobile per i numeri a doppia precisione,

La rappresentazione standard in virgola mobile a precisione doppia IEEE richiede una parola a 64 bit, che può essere rappresentata come numerata da 0 a 63, da sinistra a destra

inserisci qui la descrizione dell'immagine dove,

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

Se E=2047(tutti Esono 1) ed Fè diverso da zero, allora V=NaN("Non un numero")

Che significa,

Se tutti i Ebit sono 1 e se è presente un bit diverso da zero, Fil numero è NaN.

pertanto, tra gli altri, tutti i seguenti numeri sono NaN,

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

In particolare, non è possibile eseguire il test

if (x == Double.NaN) 

per verificare se un determinato risultato è uguale Double.NaN, perché tutti i valori "non un numero" sono considerati distinti. Tuttavia, è possibile utilizzare il Double.isNaNmetodo:

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

3

NaN è un valore speciale che indica "non un numero"; è il risultato di alcune operazioni aritmetiche non valide, come ad esempio sqrt(-1), e ha la proprietà (a volte fastidiosa) che NaN != NaN.


2

Non un numero rappresenta il risultato di operazioni il cui risultato non è rappresentabile con un numero. L'operazione più famosa è 0/0, il cui risultato non è noto.

Per questo motivo, NaN non è uguale a nulla (compresi altri valori non numerici). Per maggiori informazioni, basta consultare la pagina di Wikipedia: http://it.wikipedia.org/wiki/NaN


-1: non rappresenta il risultato di 0/0. 0/0è sempre NaN, ma NaN può essere il risultato di altre operazioni - come ad esempio 2+NaN: an operation that has no mathematically definite result produces NaN, secondo la risposta @AdrianMitev
ANeves

In effetti, NaN sta per "Not a Number" ed è il risultato di tutte le operazioni che hanno come risultato un valore indefinito o non rappresentabile. L'operazione più famosa e comune è 0/0, ma ovviamente ci sono tonnellate di altre operazioni che hanno lo stesso risultato. Sono d'accordo che la mia risposta potrebbe essere migliorata, ma non sono d'accordo sul -1 ... Ho appena verificato che anche Wikipedia utilizza le operazioni 0/0 come primo esempio di operazione con un risultato NaN ( en.wikipedia.org/wiki/ NaN ).
Matteo,

Inoltre, questo è nel sorgente Java per Double: public static final double NaN = 0.0d / 0.0;
Guillaume,

1
@Matteo +0, ora che l'istruzione falsa è sparita. E il mio -1 o +1 non è per te essere d'accordo o in disaccordo; ma è bene lasciare un commento con un -1, in modo che l'autore possa capire perché la sua risposta è considerata inutile - e cambiarla, se lo desidera.
ANeves,

@Guillaume se quel commento era destinato a me, ti preghiamo di riformularlo: non lo capisco.
ANeves,

0

Secondo questo link , ha varie situazioni e difficili da ricordare. È così che li ricordo e li distinguo. NaNsignifica "matematicamente indefinito", ad esempio: "il risultato di 0 diviso per 0 non è definito" e poiché non è definito, quindi "il confronto correlato a indefinito è ovviamente indefinito". Inoltre, funziona più come premesse matematiche. D'altra parte, sia l'infinito positivo che quello negativo sono predefiniti e definitivi, ad esempio "l'infinito positivo o negativo è ben definito matematicamente".

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.