2009-11-12 12 views
33

Stavo osservando l'implementazione di compare(double, double) nella libreria standard Java (6). Si legge:Perché la funzione Double.compare (double, double) di Java è implementata così com'è?

public static int compare(double d1, double d2) { 
    if (d1 < d2) 
     return -1;  // Neither val is NaN, thisVal is smaller 
    if (d1 > d2) 
     return 1;  // Neither val is NaN, thisVal is larger 

    long thisBits = Double.doubleToLongBits(d1); 
    long anotherBits = Double.doubleToLongBits(d2); 

    return (thisBits == anotherBits ? 0 : // Values are equal 
      (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) 
      1));       // (0.0, -0.0) or (NaN, !NaN) 
} 

Quali sono i vantaggi di questa implementazione?


modifica: "Meriti" era una (molto) brutta scelta di parole. Volevo sapere come funziona.

+1

Correlato a questo e a tutta la discussione qui: controlla questo po 'di orribilezza di JDK nell'area del confronto in virgola mobile: http://publicobject.com/2009/11/floating-point-equality.html –

+0

@KevinBourrillion - Il modo in cui l'ho letto, riguarda in realtà l'orribilità dello standard in virgola mobile IEE. Java deve comportarsi secondo lo standard IEE, perché questo è ciò che l'hardware su una macchina moderna implementa. –

risposta

38

@ risposta di Shoover è corretto, ma c'è un po 'più ad esso che questo.

quanto javadoc per Double::equals stati:

"Questa definizione permette tabelle hash di operare correttamente."

Supponiamo che i progettisti di Java hanno deciso di implementare equals(...) e compare(...) con la stessa semantica == sulle avvolti double istanze. Ciò significherebbe che equals() restituirebbe sempre false per un NaN impacchettato.Considerare ora cosa succederebbe se si tentasse di utilizzare un NaN avvolto in una mappa o collezione.

List<Double> l = new ArrayList<Double>(); 
l.add(Double.NaN); 
if (l.contains(Double.NaN)) { 
    // this wont be executed. 
} 

Map<Object,String> m = new HashMap<Object,String>(); 
m.put(Double.NaN, "Hi mum"); 
if (m.get(Double.NaN) != null) { 
    // this wont be executed. 
} 

Non ha molto senso!

Esistono altre anomalie perché -0.0 e +0.0 hanno motivi di bit diversi ma sono uguali in base a ==.

Quindi i progettisti Java hanno deciso (giustamente IMO) sulla definizione più complicata (ma più intuitiva) di questi metodi Double che abbiamo oggi.

+0

Grazie, Stephen. Sto segnando la tua risposta corretta invece di shoover perché volevo sapere perché i progettisti Java hanno messo a confronto il loro modo di fare (cioè "i meriti"), non solo quello che ha fatto il codice (anche se non lo sapevo neanche io). Le mie scuse per il fatto di non essere più definito, ma grazie a entrambi. – DavidS

+0

A causa di problemi di accuratezza in virgola mobile, non mi aspetto altro che problemi dal codice che li utilizza come chiavi di ricerca. –

+0

@ 280228 - beh sì, i tasti >> potrebbero << essere generati in un modo che è insensibile agli errori di arrotondamento, ecc. Il punto è che se qualcuno ha creato un'applicazione che evitasse il problema degli errori in virgola mobile, si aspetterebbe i tipi di raccolta per funzionare. –

5

Penso che il merito principale sia che è corretto.

Gestisce correttamente NaN e zeri firmati.

+1

Scuse, la mia domanda avrebbe dovuto essere più descrittiva. Stavo cercando una risposta come quella di Stephen. – DavidS

0

Tale implementazione consente di definire un numero reale come < NaN e -0,0 come < 0,0.

43

La spiegazione è nei commenti nel codice. Java ha valori doppi sia per 0.0 e -0.0, sia per "non un numero" (NaN). Non è possibile utilizzare l'operatore == per questi valori. Dare uno sguardo alla fonte doubleToLongBits() ea the Javadoc for the Double.equals() method:

Nota che nella maggior parte dei casi, per due istanze della classe 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, quindi 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, il test ha il valore pari false, anche se +0.0 == -0.0 ha il valore true.
2

Il merito è che è il codice più semplice che soddisfa le specifiche.

Una caratteristica comune dei programmatori principianti è quella di sopravvalutare la lettura del codice sorgente e sottovalutare la lettura delle specifiche . In questo caso, la specifica:

http://java.sun.com/javase/6/docs/api/java/lang/Double.html#compareTo%28java.lang.Double%29

... rende il comportamento e la motivo per il comportamento (coerenza con uguali()) perfettamente chiaro.

+0

Hmm, hai ragione, avrei dovuto leggerlo più da vicino, ma non dovresti assumere che sottovaluti le specifiche. Ho iniziato con il Javadoc per il confronto (...), poi sono passato a doubleToLongBits (...), e poi ho controllato l'articolo di Wikipedia su IEEE 754, momento in cui avevo dimenticato tutto sulla menzione precedente di compareTo (.. .). Quindi, penso che dovrai essere d'accordo, sopravvalutare il codice sorgente non è un mio problema, ma leggere in modo inefficiente le specifiche è! p.s. Il tuo link è rotto – DavidS