2016-06-17 13 views
9

Więc niedawno zacząłem kochać język kotlin. Dzisiaj, porównując duble, natknąłem się na nieuniknione NaN.Porównanie NaN w Kotlin

fun main(args: Array<String>) { 
    val nan = Double.NaN 
    println("1: " + (nan == nan)) 
    println("2: " + (nan == (nan as Number))) 
    println("3: " + ((nan as Number) == nan)) 
} 

Uwaga: (Doublejest podtypemNumber)

Przeprowadzenie powyższych wydajności kod:

1: false 
2: true 
3: true 

I zrozumieć, że comparing z NaN Java powraca false , więc oczekiwałbym false dla wszystkich wyrażeń.

Jak to zachowanie można wyjaśnić? Jakie są przesłanki tego działania?

Odpowiedz

8

To dlatego (2) i (3) są kompilowane do boksu prymitywne, a następnie Double.equals sprawdzić: na JVM, prymitywny double nie może być porównywana do pudełkowej jeden.

Double.equals z kolei sprawdza równość porównując doubleToLongBits(...) dwóch Double s, a ten ostatni nie ma gwarancji, że

Jeśli argument jest NaN, wynik jest 0x7ff8000000000000L.

Więc, bity wrócił na dwa NaN są równe, a reguła NaN != NaN jest ignorowany tutaj.

Ponadto, jak @miensol wspomniano, istnieje inny konsekwencją tej kontroli równości: +0 i -0 są równe według == kontroli i nie equals czeku.

Odpowiednik kodu w Javie byłoby:

double nan = Double.NaN; 
System.out.println("1: " + (nan == nan)) //false 
System.out.println("2: " + ((Double) nan).equals(((Number) nan))) 
System.out.println("3: " + ((Number) nan).equals(nan)); 

Dwie ostatnie linie nazywają Double.equals, porównując doubleToLongBits(...).

+2

myślę, że odpowiedź byłaby pełniejsza, jeśli wymienić następujące linię z [dokumentacja] (https://docs.oracle.com/javase/7/docs/api/java/lang/ Double.html # doubleToLongBits (double)) z 'doubleToLongBits': ** oprócz wszystkich wartości NaN są zwinięte do pojedynczej" kanonicznej "wartości NaN **. W przeciwnym razie można by pomyśleć, że dwa różne 'NaN 'będą porównywać do false z tą funkcją. – nfs

+0

@nrohwer, dzięki za uwagę, zaktualizowałeś odpowiedź. – hotkey

7

Pierwsze porównanie jest równoważne Java:

double left = Double.NaN; 
double right = Double.NaN; 
boolean result = left == right; 

I jak można read in this answer to wprowadził jednolity i udokumentowane zachowanie.

Drugi & trzeci porównania są równoważne:

Double left = Double.valueOf(Double.NaN); 
Number right = Double.valueOf(Double.NaN); 
boolean result = left.equals(right); 

który wykorzystuje Double.equals:

Należy zauważyć, że w większości przypadków, dla dwóch przypadków class Double, d1 i d2, roku wartość d1.equals(d2) jest prawdziwe wtedy i tylko wtedy, gdy d1.doubleValue() == d2.doubleValue() ma wartość true. Jednak istnieją dwa wyjątki:

  • Jeśli d1 i d2 zarówno reprezentować Double.NaN, wówczas równi metoda zwraca true, choć Double.NaN==Double.NaN ma wartość false.

  • Jeśli d1 oznacza +0.0 podczas d2 oznacza -0.0, lub vice versa, równe testu wartość false, chociaż +0.0==-0.0 ma wartość true .