2013-07-27 11 views
20

sto leggendo java efficace da Joshua Bloch e in Articolo 8: rispettare il contratto generale, quando si esegue l'override uguale, questa affermazione è scrittoPerché non possiamo usare '==' per confrontare due float o doppi numeri

per i campi float, utilizzare il metodo Float.compare; e per i campi doppi, utilizzare Double.compare. Il trattamento speciale dei campi float e double viene effettuato da necessario per l'esistenza di Float.NaN, -0.0f e le analoghe costanti doppie ;

Qualcuno mi può spiegare con l'esempio il motivo per cui non possiamo usare == per float o doppio confronto

+2

È un NaN uguale a qualsiasi altro NaN? È maggiore di, inferiore o uguale a qualsiasi altro numero? -0.0f è uguale a 0.0f? –

+0

http://stackoverflow.com/a/9341669/1276341 – MrLore

+0

Dipende da cosa vuoi fare. IIRC (non trattenermi a questo) 'NaN == NaN' è falso, es. Ma le specifiche ufficiali di 'compare' non ti danno molte informazioni su come vengono gestiti i casi" oddball ". Più importante è ricordare di non confrontare mai per "uguale", tranne nei casi speciali in cui sai cosa stai facendo. –

risposta

5

float (e double) hanno alcune sequenze speciali bit che sono riservati per significati speciali che non sono "numeri" :

  • infinito negativo, rappresentazione interna 0xff800000
  • infinito positivo, rappresentazione interna 0x7f800000
  • Not a Number, rappresentazione interna 0x7fc00000

Ognuno di questi rendimenti 0 (nel senso che sono "lo stesso") rispetto a se stessa utilizzando Float.compare(), ma i seguenti confronti usando == differiscono da questo per Float.NaN:

Float.NEGATIVE_INFINITY == Float.NEGATIVE_INFINITY // true 
Float.POSITIVE_INFINITY == Float.POSITIVE_INFINITY // true 
Float.NaN == Float.NaN // false 

Così quando si confrontano float valori, per essere coerenti per tutti i valori, compreso il valore speciale Float.NaN, Float.compare() è l'opzione migliore.

Lo stesso vale per double.

+0

@EricPostpischil 'Float.compare()' restituisce un 'int', non" false "(o" true "). Tutte le chiamate successive a 'Float.compare()' restituiscono '0':' Float.compare (Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY) ',' Float.compare (Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY) ',' Float.compare (Float.NaN, Float.NaN) '. Sono sicuro di questo perché l'ho appena eseguito. – Bohemian

+0

@EricPostpischil OK - Ho modificato la risposta per spiegare cosa significa zero (anche se è nella javadoc di 'compare()') – Bohemian

16

Da apidoc, Float.compare:

confronta i due valori float specificati. Il segno del valore intero restituito è lo stesso di quello del numero intero che verrebbe restituito dalla chiamata:

nuovo Float (f1) .compareTo (nuovo Float (f2))

Float.compareTo:

Confronta numericamente due oggetti Float. Ci sono due modi in cui confronti eseguiti con tale metodo differiscono da quelle eseguite dagli operatori di confronto numerico linguaggio Java (<, < =, ==,> =>) quando applicato ai valori float primitive:

  • galleggiante. NaN è considerato da questo metodo uguale a se stesso e maggiore di tutti gli altri valori float (incluso Float.POSITIVE_INFINITY).
  • 0.0f è considerato da questo metodo maggiore di -0.0f.

Ciò garantisce che l'ordine naturale degli oggetti Float imposti da questo metodo sia coerente con gli uguali.

Si consideri il seguente codice:

System.out.println(-0.0f == 0.0f); //true 
    System.out.println(Float.compare(-0.0f, 0.0f) == 0 ? true : false); //false  
    System.out.println(Float.NaN == Float.NaN);//false 
    System.out.println(Float.compare(Float.NaN, Float.NaN) == 0 ? true : false); //true 
    System.out.println(-0.0d == 0.0d); //true 
    System.out.println(Double.compare(-0.0d, 0.0d) == 0 ? true : false);//false  
    System.out.println(Double.NaN == Double.NaN);//false 
    System.out.println(Double.compare(Double.NaN, Double.NaN) == 0 ? true : false);//true   

L'output non è corretto, in quanto qualcosa che non è un numero, non è semplicemente un numero, e deve essere trattato come uguale dal punto di confronto il numero di vista. È anche chiaro che 0=-0.

Vediamo cosa Float.compare fa:

public static int compare(float f1, float f2) { 
    if (f1 < f2) 
     return -1;   // Neither val is NaN, thisVal is smaller 
    if (f1 > f2) 
     return 1;   // Neither val is NaN, thisVal is larger 

    int thisBits = Float.floatToIntBits(f1); 
    int anotherBits = Float.floatToIntBits(f2); 

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

Float.floatToIntBits:

restituisce una rappresentazione del valore a virgola mobile specificato in base a virgola mobile IEEE 754 "formato single" la layout bit . Il bit 31 (il bit selezionato dalla maschera 0x80000000) rappresenta il segno del numero a virgola mobile. I bit 30-23 (i bit selezionati dalla maschera 0x7f800000) rappresentano l'esponente. I bit 22-0 (i bit selezionati dalla maschera 0x007fffff) rappresentano il significato e (talvolta chiamato la mantissa) del numero a virgola mobile.

Se l'argomento è infinito positivo, il risultato è 0x7f800000.

Se l'argomento è infinito negativo, il risultato è 0xff800000.

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

In tutti i casi, il risultato è un intero che, quando somministrato al metodo intBitsToFloat (int), produrrà un valore di virgola mobile stesso come argomento di floatToIntBits (eccetto tutti valori NaN sono collassati per un singolo "canonico" valore NaN).

Da JLS 15.20.1. Numerical Comparison Operators <, <=, >, and >=

Il risultato di un confronto in virgola mobile, come determinato dalle specifiche dello standard IEEE 754, è:

  • Se uno degli operandi è NaN, allora il risultato è falso

  • Tutti i valori diversi da NaN sono ordinati, con infinito negativo minore di tutti i valori finiti e infinito positivo maggiore di tutti i valori finiti.

  • Lo zero positivo e lo zero negativo sono considerati uguali. Ad esempio, -0,0 < 0,0 è falso, ma -0,0 < = 0,0 è vero.

  • Si noti, tuttavia, che i metodi Math.min e Math.max trattano lo zero negativo come strettamente inferiore allo zero positivo.

Per i confronti severe dove operandi sono zero positivo e zero negativo il risultato sarà sbagliato.

Da JLS 15.21.1. Numerical Equality Operators == and !=:

Il risultato di un confronto in virgola mobile, come determinato dalle specifiche dello standard IEEE 754, è: test di uguaglianza

virgola mobile viene eseguita secondo la regole dello standard IEEE 754:

  • Se uno degli operandi è NaN, allora il risultato del == è falso, ma il risultato di = è vero!. In effetti, il test x! = X è vero se e solo se il valore di x è NaN. I metodi Float.isNaN e Double.isNaN possono anche essere utilizzati per verificare se un valore è NaN.

  • Lo zero positivo e lo zero negativo sono considerati uguali. Ad esempio, -0.0 == 0.0 è vero.

  • In caso contrario, due valori distinti a virgola mobile vengono considerati non uguali dagli operatori di uguaglianza. In particolare, esiste un valore che rappresenta l'infinito positivo e un valore che rappresenta l'infinito negativo; ognuno confronta uguale solo a se stesso, e ciascuno confronta ineguale con tutti gli altri valori.

Per i confronti di uguaglianza in cui entrambi gli operandi sono NaN il risultato sarà sbagliato.

Poiché il total ordering (=, <, >,<=, >=) è utilizzato da molti algoritmi importanti (vedere all the classes that implement the Comparable interface) è preferibile utilizzare il metodo di confronto in quanto produce un comportamento più coerente.

La conseguenza dello total ordering in the context of the IEEE-754 standard è la differenza tra lo zero positivo e negativo.

Ad esempio, se si utilizza l'operatore di uguaglianza anziché il metodo di confronto e si dispone di una raccolta di valori e la logica del codice prende alcune decisioni in base all'ordinamento degli elementi e in qualche modo si inizia a ottenere un surplus di valori NaN saranno tutti trattati come valori diversi invece degli stessi valori.

Ciò potrebbe produrre un errore nel comportamento del programma proporzionale alla quantità/velocità dei valori NaN. E se hai molti zeri positivi e negativi, è solo una coppia che incide sulla tua logica con errori.

Float uses Formato IEEE-754 a 32 bit e doppio uses Formato 64 bit IEEE-754.

1

Ci sono due motivi per confrontare gli oggetti in virgola mobile:

  • che sto facendo la matematica, quindi voglio confrontare i loro valori numerici. Numericamente, -0 è uguale a +0 e un NaN non è uguale a nulla, nemmeno a se stesso, perché "uguale" è una proprietà che solo i numeri hanno e NaN non è un numero.
  • Sto lavorando con oggetti in un computer, quindi ho bisogno di distinguere diversi oggetti e metterli in ordine. Questo è necessario per ordinare gli oggetti in un albero o in un altro contenitore, per esempio.

L'operatore == fornisce confronti matematici. Esso restituisce false per NaN == NaN e vero per -0.f == +0.f

I compare e compareTo routine forniscono confronti degli oggetti. Quando si confronta un NaN con se stesso, indicano che è lo stesso (restituendo zero). Quando si confrontano -0.f in +0.f, indicano che sono diversi (restituendo non zero).