2016-06-17 17 views
9

Così recentemente ho iniziato ad amare il linguaggio Kotlin. Oggi, confrontando i doppi, mi sono imbattuto nell'inevitabile NaN.Confronto di 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: (Doubleè un sottotipo diNumber)

Esecuzione dei rendimenti di codice di cui sopra:

1: false 
2: true 
3: true 

Capisco che comparing con NaN in Java restituisce false , quindi mi aspetterei false per tutte le espressioni.

Come si spiega questo comportamento? Qual è la logica dietro di esso?

risposta

8

Questo perché (2) e (3) sono compilati alla boxe un primitivo e poi Double.equals controllo: sulla JVM, primitivo double non può essere paragonato ad uno scatolato.

Double.equals, a sua volta, controlla l'uguaglianza confrontando doubleToLongBits(...) dei due Double s, e per quest'ultimo c'è una garanzia che

Se l'argomento è NaN, il risultato è 0x7ff8000000000000L.

Così, i bit restituiti per due NaN sono uguali, e la regola NaN != NaN viene ignorato qui.

Inoltre, come @miensol accennato, c'è un'altra conseguenza di questo controllo di uguaglianza: +0 e -0 sono uguali secondo == controllo e non equals controllo.

codice equivalente in Java sarebbe:

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

Le ultime due righe chiamano Double.equals, confrontando doubleToLongBits(...).

+2

Credo che la vostra risposta sarebbe più completa se lei ha citato la seguente riga dal [documentazione] (https://docs.oracle.com/javase/7/docs/api/java/lang/ Double.html # doubleToLongBits (double)) di 'doubleToLongBits': ** tranne che tutti i valori NaN sono compressi in un singolo valore NaN" canonico "**. Altrimenti si potrebbe pensare che due diversi 'NaN's sarebbero paragonabili a quelli falsi con quella funzione. – nfs

+0

@nrohwer, grazie per il tuo commento, ha aggiornato la risposta. – hotkey

7

Il primo confronto è equivalente a Java:

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

E come si può read in this answer Questo è normalizzata e il comportamento documentato.

Il secondo & terzo confronto sono equivalenti a:

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

che utilizza Double.equals:

Nota che nella maggior parte dei casi, per due istanze di class Double, d1 e d2, il valore di d1.equals(d2) è true se e solo se d1.doubleValue() == d2.doubleValue() ha anche il valore true. Tuttavia, ci sono due eccezioni:

  • Se d1 e d2 entrambi rappresentano Double.NaN, allora il metodo equals visualizzare nuovamente true, anche se Double.NaN==Double.NaN ha il valore false.

  • Se d1 rappresenta +0.0 mentre d2 rappresenta -0.0, o viceversa, la prova pari ha il valore false, anche se +0.0==-0.0 ha il valore true .