2016-06-17 13 views
9

So begann ich vor kurzem die Sprache Kotlin zu lieben. Heute, während ich Doubles vergleiche, bin ich auf das unvermeidliche NaN gestoßen.Vergleichen NaN in 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)) 
} 

NB: (Doubleist ein Subtyp vonNumber)

die oben genannten Code Ausbeuten Lauf:

1: false 
2: true 
3: true 

Ich verstehe, dass comparing mit NaN in Java gibt false , also würde icherwartenfür alle Ausdrücke.

Wie kann dieses Verhalten erklärt werden? Was ist der Grund dafür?

Antwort

8

Das ist, weil (2) und (3) zusammengestellt sind ein primitives zum Boxen und dann Double.equals Kontrolle: auf JVM, primitive double kann nicht auf eine Box verglichen werden.

Double.equals, die wiederum prüft die Gleichheit von doubleToLongBits(...) der beiden Double s zu vergleichen, und für letztere eine Garantie gibt es, dass

Wenn das Argument NaN ist, ist das Ergebnis 0x7ff8000000000000L ist.

So kehrten die Bits für zwei NaN gleich sind, und die Regel NaN != NaN wird hier ignoriert.

Auch als @miensol erwähnt, gibt es eine weitere Folge dieser Gleichheitsprüfung: +0 und -0 sind gleich nach == Prüfung und nicht zu equals überprüfen.

Equivalent Code in Java wäre:

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)); 

Die letzten beiden Zeilen Double.equals nennen, zu vergleichen doubleToLongBits(...).

+2

Ich denke, die Antwort wäre vollständig, wenn Sie die folgende Zeile aus der [Dokumentation] erwähnt (https://docs.oracle.com/javase/7/docs/api/java/lang/ Double.html # doubleToLongBits (double)) von 'doubleToLongBits': ** Ausnahme: alle NaN-Werte werden auf einen einzelnen" kanonischen "NaN-Wert reduziert **. Sonst könnte man meinen, dass zwei verschiedene NaN mit dieser Funktion mit false verglichen würden. – nfs

+0

@nrohwer, danke für deine Bemerkung, habe die Antwort aktualisiert. – hotkey

7

Der erste Vergleich entspricht Java:

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

Und wie Sie können read in this answer diese standardisiert ist und das Verhalten dokumentiert.

Der zweite & dritten Vergleich gleichwertig sind:

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

Welche Double.equals verwendet:

Beachten Sie, dass in den meisten Fällen für zwei Instanzen von class Double, d1 und d2, der Wert d1.equals(d2) ist genau dann wahr, wenn d1.doubleValue() == d2.doubleValue() auch den Wert wahr hat. Es gibt jedoch zwei Ausnahmen :

  • Wenn d1 und d2 beide Double.NaN darstellen, dann ist die Gleichen Methode gibt true, obwohl Double.NaN==Double.NaN den Wert false hat.

  • Wenn d1 repräsentiert +0.0 während d2-0.0 darstellt, oder umgekehrt, die gleich Test hat den Wert false, obwohl +0.0==-0.0 hat den Wert true.