2013-07-27 8 views
20

I Effective Java von Joshua Bloch lese und in Punkt 8: die allgemeinen Vertrags Obey, wenn zwingende entspricht diese Aussage geschriebenWarum können wir nicht ‚==‘ verwenden zwei float oder double Zahlen vergleichen

für Float-Felder verwenden Sie die Float.compare-Methode; und für doppelte Felder verwenden Sie Double.compare. Die spezielle Behandlung von float- und double-Feldern erfolgt notwendig durch die Existenz von Float.NaN, -0.0f und den analogen Doppel Konstanten;

Kann jemand mir erklären, mit Beispiel, warum wir nicht == für Schwimmer oder Doppel Vergleich

+2

Ist ein NaN gleich einem anderen NaN? Ist es größer als, kleiner oder gleich einer anderen Zahl? Ist -0.0f gleich 0.0f? –

+0

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

+0

Es hängt davon ab, was Sie tun möchten. IIRC (halte mich nicht an) NaN == NaN ist falsch, z. Aber die offizielle Spezifikation von 'compare' gibt dir nicht viele Informationen darüber, wie die" Oddball "-Fälle gehandhabt werden. Wichtiger ist, sich daran zu erinnern, nie für "gleich" zu vergleichen, außer in speziellen Fällen, in denen Sie wissen, was Sie tun. –

Antwort

5

float (und double) haben einige spezielle Bitfolgen verwenden können, die für eine besondere Bedeutung reserviert sind, die nicht „Zahlen“ sind :

  • Negative Unendlichkeit, interne Darstellung 0xff800000
  • Positive Unendlichkeit, interne Darstellung 0x7f800000
  • Not a Number, interne Darstellung 0x7fc00000

Jede dieser Erträge 0 (das heißt, sie „das gleiche“) sind im Vergleich zu den sich aus dieser Float.compare(), aber die folgenden Vergleiche mit == unterscheiden sich für Float.NaN mit:

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

wenn float Werte zu vergleichen, konsistent zu sein für alle Werte, einschließlich der speziellen Float.NaN Wert, Float.compare() ist die beste Option.

Das gleiche gilt für double.

+0

@EricPostpischil 'Float.compare()' gibt ein 'int', nicht" false "(oder" true ") zurück. Alle folgenden Aufrufe von 'Float.compare()' return '0':' Float.compare (Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY) ',' Float.compare (Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY) ',' Float.compare (Float.NaN, Float.NaN) '. Ich bin mir dessen sicher, weil ich es gerade ausgeführt habe. – Bohemian

+0

@EricPostpischil OK - Ich habe die Antwort bearbeitet, um zu erklären, was Null bedeutet (obwohl es im Javadoc von 'compare()' ist) – Bohemian

16

Von apidoc, Float.compare:

Vergleicht die beiden angegebenen Float-Werte. Das Vorzeichen des ganzzahligen Wert zurückgegeben wird, ist die gleiche wie die der ganzen Zahl, die durch den Aufruf zurückgegeben werden würde:

neue Float (f1) .compareTo (new Float (f2))

Float.compareTo:

Vergleicht zwei Float-Objekte numerisch. Es gibt zwei Möglichkeiten, in denen nach dieser Methode durchgeführt Vergleiche aus, die durch die Java-Sprache numerischen Vergleichsoperatoren durchgeführt unterscheiden (<, < =, ==,> =>), wenn auf primitive float-Werte:

  • Float. NaN wird bei dieser Methode als gleich groß und größer als alle anderen Float-Werte (einschließlich Float.POSITIVE_INFINITY) betrachtet.
  • 0.0f wird von dieser Methode als größer als -0.0f betrachtet.

Dies stellt sicher, dass die natürliche Ordnung der Float-Objekte durch dieses Verfahren verhängten mit equals konsistent ist.

Betrachten Sie den folgenden Code ein:

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   

Der Ausgang ist nicht korrekt, da etwas, das keine Zahl ist, ist einfach nicht eine Zahl ist, und soll gleich von Nummer Vergleichspunkt behandelt werden, von Aussicht. Es ist auch klar, dass 0=-0.

Mal sehen, was Float.compare tut:

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:

Gibt eine Darstellung des angegebenen Gleitkommawert nach dem IEEE 754 Floating-Point "single-Format" Bit-Layout . Bit 31 (das Bit, das von der Maske 0x80000000 ausgewählt wird) stellt das Vorzeichen der Fließkommazahl dar. Die Bits 30-23 (die Bits, die von der Maske 0x7f800000 ausgewählt werden) repräsentieren den Exponenten. Die Bits 22-0 (die Bits, die durch die Maske 0x007fffff ausgewählt werden) repräsentieren die Mantisse der Gleitkommazahl.

Wenn das Argument positiv unendlich ist, lautet das Ergebnis 0x7f800000.

Wenn das Argument negativ unendlich ist, lautet das Ergebnis 0xff800000.

Wenn das Argument NaN ist, ist das Ergebnis 0x7fc00000. In allen Fällen ist das Ergebnis eine ganze Zahl, die, wenn sie der intBitsToFloat (int) -Methode übergeben wird, einen Gleitkommawert erzeugt, der dem Argument von floatToIntBits entspricht (, außer dass alle NaN-Werte auf einen einzigen Wert reduziert sind) "kanonischer" NaN-Wert).

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

Das Ergebnis einer Gleitkomma-Vergleich, wie sie in der Spezifikation der Norm IEEE 754 bestimmt, ist:

  • Falls einer der Operanden NaN ist, dann Das Ergebnis ist falsch.

  • Alle anderen Werte als NaN sind geordnet, wobei die negative Unendlichkeit kleiner als alle endlichen Werte und die positive Unendlichkeit größer als alle endlichen Werte ist.

  • Positive Null und negative Null werden als gleich betrachtet. Zum Beispiel ist -0.0 < 0.0 falsch, aber -0.0 < = 0.0 ist wahr.

  • Beachten Sie jedoch, dass die Methoden Math.min und Math.max den negativen Nullwert als streng kleiner als den positiven Nullwert behandeln.

Für strenge Vergleiche wo Operanden sind positive Null und negative Null das Ergebnis falsch sein wird.

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

Das Ergebnis eines Gleitkomma-Vergleich, wie sie in der Spezifikation der Norm IEEE 754 bestimmt, ist:

Gleitkommazahl Gleichheit Prüfung wird in Übereinstimmung mit der durchgeführt Regeln der IEEE-Norm 754:

  • Wenn einer der Operanden NaN, dann ist das Ergebnis von == ist falsch, aber das Ergebnis ist = ist wahr!. In der Tat ist der Test x! = X genau dann wahr, wenn der Wert von x NaN ist. Die Methoden Float.isNaN und Double.isNaN können auch verwendet werden, um zu testen, ob ein Wert NaN ist.

  • Positive Null und negative Null werden als gleich betrachtet. Zum Beispiel ist -0.0 == 0.0 wahr.

  • Andernfalls werden zwei unterschiedliche Gleitkommawerte von den Gleichheitsoperatoren als ungleich betrachtet. Insbesondere gibt es einen Wert für die positive Unendlichkeit und einen Wert für die negative Unendlichkeit. Jeder vergleicht sich nur mit sich selbst, und jeder vergleicht ungleich mit allen anderen Werten.

Für Gleichheitsvergleiche, wo beide Operanden NaN wird das Ergebnis falsch sein.

Da total ordering (=, <, >,<=, >=) von vielen wichtigen Algorithmen verwendet wird (siehe all the classes that implement the Comparable interface), ist es besser, die Vergleichsmethode zu verwenden, da dies zu einem konsistenteren Verhalten führt.

Die Konsequenz der total ordering in the context of the IEEE-754 standard ist die Differenz zwischen dem positiven und negativen Nullpunkt. Wenn Sie beispielsweise den Gleichheitsoperator anstelle der Vergleichsmethode verwenden und eine Sammlung von Werten haben und Ihre Codelogik einige Entscheidungen basierend auf der Reihenfolge der Elemente trifft, erhalten Sie irgendwie einen Überschuss an NaN-Werten Sie werden alle als unterschiedliche Werte anstatt als dieselben Werte behandelt.

Dies kann wahrscheinlich Fehler im Verhalten des Programms proportional zur Menge/Rate der NaN-Werte erzeugen. Und wenn Sie viele positive und negative Nullen haben, ist das nur ein Paar, das Ihre Logik mit Fehlern beeinflusst.

Float uses IEEE-754 32-Bit-Format und Double uses IEEE-754 64-Bit-Format.

1

Es gibt zwei Gründe Gleitkommazahlen Objekte zu vergleichen:

  • ich Mathe tue, so will ich ihre Zahlenwerte vergleichen. Numerisch ist -0 gleich +0, und ein NaN ist nichts, nicht einmal selbst, denn "gleich" ist eine Eigenschaft, die nur Zahlen haben, und NaN ist keine Zahl.
  • Ich arbeite mit Objekten in einem Computer, also muss ich verschiedene Objekte unterscheiden und sie in der richtigen Reihenfolge platzieren. Dies ist zum Beispiel für das Sortieren von Objekten in einem Baum oder einem anderen Container erforderlich.

Der Operator == bietet mathematische Vergleiche. Es gibt false für NaN == NaN und gilt für -0.f == +0.f

Die compare und compareTo Routinen Objektvergleiche bieten. Wenn sie ein NaN mit sich selbst vergleichen, zeigen sie an, dass es dasselbe ist (indem sie Null zurückgibt). Wenn sie -0.f mit +0.f vergleichen, zeigen sie an, dass sie unterschiedlich sind (indem sie nicht null zurückgeben).