Laut dieser java.sun-Seite ==
ist der Gleichheitsvergleichsoperator für Gleitkommazahlen in Java.
Wenn ich jedoch diesen Code eingebe:
if(sectionID == currentSectionID)
In meinem Editor und bei der statischen Analyse wird Folgendes angezeigt: "JAVA0078 Gleitkommawerte im Vergleich zu =="
Was ist falsch ==
daran, Gleitkommawerte zu vergleichen? Was ist der richtige Weg, um es zu tun?
java
equality
floating-accuracy
user128807
quelle
quelle
Antworten:
Der richtige Weg, um Floats auf "Gleichheit" zu testen, ist:
Dabei ist epsilon eine sehr kleine Zahl wie 0,00000001, abhängig von der gewünschten Genauigkeit.
quelle
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
, um dieses Problem anzugehen?Math.ulp()
in meiner Antwort auf diese Frage zu verwenden.Gleitkommawerte können geringfügig abweichen, sodass sie möglicherweise nicht genau gleich sind. Wenn Sie beispielsweise einen Float auf "6.1" setzen und ihn dann erneut ausdrucken, erhalten Sie möglicherweise einen gemeldeten Wert von "6.099999904632568359375". Dies ist von grundlegender Bedeutung für die Funktionsweise von Floats. Daher möchten Sie sie nicht mit Gleichheit vergleichen, sondern innerhalb eines Bereichs vergleichen, dh wenn der Unterschied des Floats zu der Zahl, mit der Sie ihn vergleichen möchten, kleiner als ein bestimmter absoluter Wert ist.
Dieser Artikel im Register gibt einen guten Überblick darüber, warum dies der Fall ist. nützliche und interessante Lektüre.
quelle
Nur um den Grund für das zu nennen, was alle anderen sagen.
Die binäre Darstellung eines Floats ist irgendwie nervig.
In der Binärdatei kennen die meisten Programmierer die Korrelation zwischen 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d
Nun, es funktioniert auch umgekehrt.
.1b = .5d, .01b = .25d, .001b = .125, ...
Das Problem ist, dass es keine genaue Möglichkeit gibt, die meisten Dezimalzahlen wie .1, .2, .3 usw. darzustellen. Alles, was Sie tun können, ist eine ungefähre Angabe in Binärform. Das System führt beim Drucken der Zahlen eine kleine Fudge-Rundung durch, sodass .1 anstelle von .10000000000001 oder .999999999999 angezeigt wird (die wahrscheinlich genauso nahe an der gespeicherten Darstellung liegen wie .1).
Aus Kommentar bearbeiten: Der Grund, warum dies ein Problem ist, sind unsere Erwartungen. Wir gehen davon aus, dass 2/3 irgendwann verfälscht wird, wenn wir es in Dezimalzahlen umwandeln, entweder .7 oder .67 oder .666667. Wir erwarten jedoch nicht automatisch, dass .1 auf die gleiche Weise wie 2/3 gerundet wird --und genau das passiert.
Übrigens, wenn Sie neugierig sind, ist die Zahl, die intern gespeichert wird, eine reine binäre Darstellung unter Verwendung einer binären "wissenschaftlichen Notation". Wenn Sie also anweisen, die Dezimalzahl 10.75d zu speichern, werden 1010b für die 10 und .11b für die Dezimalzahl gespeichert. Es würde also .101011 speichern, dann werden am Ende einige Bits gespeichert, um zu sagen: Verschieben Sie den Dezimalpunkt um vier Stellen nach rechts.
(Obwohl es technisch gesehen kein Dezimalpunkt mehr ist, ist es jetzt ein Binärpunkt, aber diese Terminologie hätte die Dinge für die meisten Menschen, die diese Antwort von Nutzen finden würden, nicht verständlicher gemacht.)
quelle
Weil das nicht stimmt
0.1 + 0.2 == 0.3
quelle
Float.compare(0.1f+0.2f, 0.3f) == 0
?Ich denke, es gibt viel Verwirrung um Schwimmer (und Doppel), es ist gut, es zu klären.
Es ist an sich nichts Falsches daran, Floats als IDs in standardkonformer JVM [*] zu verwenden. Wenn Sie einfach die Float-ID auf x setzen, nichts damit anfangen (dh keine Arithmetik) und später auf y == x testen, ist alles in Ordnung. Es ist auch nichts Falsches daran, sie als Schlüssel in einer HashMap zu verwenden. Was Sie nicht tun können, ist, Gleichheiten wie
x == (x - y) + y
usw. anzunehmen. Allerdings verwenden die Leute normalerweise ganzzahlige Typen als IDs, und Sie können beobachten, dass die meisten Leute hier von diesem Code abgeschreckt sind. Aus praktischen Gründen ist es daher besser, Konventionen einzuhalten . Beachten Sie, dass es so viele verschiedenedouble
Werte wie lange gibtvalues
, sodass Sie durch die Verwendung nichts gewinnendouble
. Das Generieren der "nächsten verfügbaren ID" kann bei Doppeln schwierig sein und erfordert einige Kenntnisse der Gleitkomma-Arithmetik. Die Mühe nicht wert.Andererseits ist es riskant, sich auf die numerische Gleichheit der Ergebnisse zweier mathematisch äquivalenter Berechnungen zu verlassen. Dies liegt an Rundungsfehlern und Genauigkeitsverlusten bei der Konvertierung von Dezimal- in Binärdarstellung. Dies wurde auf SO zu Tode diskutiert.
[*] Als ich "standardkonforme JVM" sagte, wollte ich bestimmte gehirngeschädigte JVM-Implementierungen ausschließen. Sehen Sie das .
quelle
==
anstatt verglichen werdenequals
oder dass kein Float, der nicht mit sich selbst vergleichbar ist, in einer Tabelle gespeichert wird. Andernfalls kann ein Programm, das versucht zu zählen, wie viele eindeutige Ergebnisse aus einem Ausdruck erzeugt werden können, wenn verschiedene Eingaben eingegeben werden, jeden NaN-Wert als eindeutig betrachten.Float
, nicht auffloat
.Float
? Wenn man versucht, eine Tabelle mit eindeutigenfloat
Werten zu erstellen und diese zu vergleichen==
, führen die schrecklichen IEEE-754-Vergleichsregeln dazu, dass die Tabelle mitNaN
Werten überflutet wird .float
Typ hat keineequals
Methode.equals
Instanzmethode, sondern die statische Methode (ich denke innerhalb derFloat
Klasse), die zwei Werte des Typs vergleichtfloat
.Dies ist ein Problem, das nicht spezifisch für Java ist. Die Verwendung von == zum Vergleichen von zwei Floats / Doubles / einer beliebigen Dezimaltypnummer kann aufgrund der Art und Weise, wie sie gespeichert werden, möglicherweise Probleme verursachen. Ein Float mit einfacher Genauigkeit (gemäß IEEE-Standard 754) hat 32 Bit, die wie folgt verteilt sind:
1 Bit - Vorzeichen (0 = positiv, 1 = negativ)
8 Bit - Exponent (eine spezielle (Bias-127) Darstellung des x in 2 ^ x)
23 Bit - Mantisa. Die tatsächlich gespeicherte Nummer.
Die Mantisa verursacht das Problem. Es ist ein bisschen wie eine wissenschaftliche Notation, nur die Zahl in Basis 2 (binär) sieht aus wie 1.110011 x 2 ^ 5 oder ähnliches. In der Binärdatei ist die erste 1 immer eine 1 (mit Ausnahme der Darstellung von 0).
Um ein wenig Speicherplatz zu sparen (Wortspiel beabsichtigt), entschied IEEE daher, dass die 1 angenommen werden sollte. Zum Beispiel ist eine Mantisa von 1011 wirklich 1.1011.
Dies kann zu Vergleichsproblemen führen, insbesondere bei 0, da 0 möglicherweise nicht genau in einem Float dargestellt werden kann. Dies ist der Hauptgrund, warum von == abgeraten wird, zusätzlich zu den Gleitkomma-Mathematikproblemen, die in anderen Antworten beschrieben werden.
Java hat das einzigartige Problem, dass die Sprache auf vielen verschiedenen Plattformen universell ist, von denen jede ihr eigenes Float-Format haben kann. Umso wichtiger ist es, == zu vermeiden.
Der richtige Weg, um zwei Floats (nicht sprachspezifisch) auf Gleichheit zu vergleichen, ist wie folgt:
Dabei ist ACCEPTABLE_ERROR #defined oder eine andere Konstante gleich 0,000000001 oder die erforderliche Genauigkeit, wie Victor bereits erwähnt hat.
Einige Sprachen haben diese Funktionalität oder diese Konstante eingebaut, aber im Allgemeinen ist dies eine gute Angewohnheit.
quelle
Ab heute ist der schnelle und einfache Weg, dies zu tun:
Die Dokumenten wird jedoch der Wert der Randdifferenz (ein Epsilon aus der Antwort von @Victor), der bei Berechnungen auf Floats immer vorhanden ist, nicht eindeutig angegeben. Dies sollte jedoch sinnvoll sein, da er Teil der Standard-Sprachbibliothek ist.
Wenn jedoch eine höhere oder angepasste Präzision erforderlich ist, dann
ist eine weitere Lösungsoption.
quelle
(sectionId == currentSectionId)
für Gleitkommazahlen nicht genau ist. Die Epsilon-Methode ist der bessere Ansatz, der in dieser Antwort enthalten ist: stackoverflow.com/a/1088271/4212710Schaumpunktwerte sind aufgrund eines Rundungsfehlers nicht zuverlässig.
Als solche sollten sie wahrscheinlich nicht als Schlüsselwerte verwendet werden, wie z. B. sectionID. Verwenden Sie stattdessen Ganzzahlen oder
long
wennint
nicht genügend mögliche Werte enthalten sind.quelle
double
s sind viel präziser, aber sie sind auch Gleitkommawerte, daher sollte meine Antwort sowohlfloat
als auch enthaltendouble
.Zusätzlich zu früheren Antworten sollten Sie sich bewusst sein, dass mit
-0.0f
und+0.0f
(sie sind==
aber nichtequals
) undFloat.NaN
(es istequals
aber nicht ) seltsame Verhaltensweisen verbunden sind==
) (ich hoffe, ich habe das richtig verstanden - argh, tun Sie es nicht!).Edit: Lass uns nachsehen!
Willkommen bei IEEE / 754.
quelle
==
bedeutet dies nicht, dass Zahlen "mit dem Bit identisch" sind (dieselbe Zahl kann mit unterschiedlichen Bitmustern dargestellt werden, obwohl nur eine davon in normalisierter Form vorliegt). Auch-0.0f
und0.0f
werden durch verschiedene Bitmuster dargestellt (das Vorzeichenbit ist unterschiedlich), aber vergleichen Sie als gleich mit==
(aber nicht mitequals
). Ihre Annahme, dass==
es sich um einen bitweisen Vergleich handelt, ist im Allgemeinen falsch.Hier ist eine sehr lange (aber hoffentlich nützliche) Diskussion über dieses und viele andere Gleitkomma-Probleme, auf die Sie möglicherweise stoßen: Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte
quelle
Sie können Float.floatToIntBits () verwenden.
quelle
Zuallererst schweben sie oder schweben sie? Wenn einer von ihnen ein Float ist, sollten Sie die Methode equals () verwenden. Wahrscheinlich ist es auch am besten, die statische Float.compare-Methode zu verwenden.
quelle
Folgendes verwendet automatisch die beste Präzision:
Natürlich können Sie mehr oder weniger als 5 ULPs auswählen ("Einheit an letzter Stelle").
Wenn Sie in die Apache Commons Bibliothek sind, die
Precision
hat KlassecompareTo()
undequals()
sowohl mit Epsilon und ULP.quelle
double
, um dies abzudecken.Sie möchten vielleicht, dass es == ist, aber 123.4444444444443! = 123.4444444444442
quelle
Wenn Sie Floats verwenden müssen, kann das Schlüsselwort strictfp hilfreich sein.
http://en.wikipedia.org/wiki/strictfp
quelle
Zwei verschiedene Berechnungen, die gleiche reelle Zahlen erzeugen, erzeugen nicht notwendigerweise gleiche Gleitkommazahlen. Personen, die == verwenden, um die Ergebnisse von Berechnungen zu vergleichen, werden normalerweise davon überrascht. Die Warnung hilft daher dabei, einen möglicherweise subtilen und schwer zu reproduzierenden Fehler zu kennzeichnen.
quelle
Haben Sie es mit ausgelagertem Code zu tun, der Floats für Dinge mit dem Namen sectionID und currentSectionID verwendet? Nur neugierig.
@ Bill K: "Die binäre Darstellung eines Floats ist irgendwie nervig." Wie? Wie würdest du es besser machen? Es gibt bestimmte Zahlen, die in keiner Basis richtig dargestellt werden können, weil sie niemals enden. Pi ist ein gutes Beispiel. Sie können es nur annähern. Wenn Sie eine bessere Lösung haben, wenden Sie sich an Intel.
quelle
Wie in anderen Antworten erwähnt, können Doppelte kleine Abweichungen aufweisen. Und Sie könnten Ihre eigene Methode schreiben, um sie mit einer "akzeptablen" Abweichung zu vergleichen. Jedoch ...
Es gibt eine Apache-Klasse zum Vergleichen von Doubles: org.apache.commons.math3.util.Precision
Es enthält einige interessante Konstanten:
SAFE_MIN
undEPSILON
, die die maximal möglichen Abweichungen einfacher arithmetischer Operationen sind.Es bietet auch die notwendigen Methoden zum Vergleichen, Gleichstellen oder Runden von Doppelwerten. (mit Ulps oder absoluter Abweichung)
quelle
In einer einzeiligen Antwort kann ich sagen, Sie sollten verwenden:
Damit Sie mehr über die korrekte Verwendung verwandter Operatoren erfahren, werden hier einige Fälle erläutert: Im Allgemeinen gibt es drei Möglichkeiten, Zeichenfolgen in Java zu testen. Sie können ==, .equals () oder Objects.equals () verwenden.
Wie unterscheiden sie sich? == testet die Referenzqualität in Strings, um herauszufinden, ob die beiden Objekte gleich sind. Andererseits testet .equals (), ob die beiden Zeichenfolgen logisch gleichwertig sind. Schließlich testet Objects.equals () auf Nullen in den beiden Zeichenfolgen und bestimmt dann, ob .equals () aufgerufen werden soll.
Idealer Bediener
Nun, dies war Gegenstand vieler Debatten, da jeder der drei Betreiber seine eigenen Stärken und Schwächen hat. Beispiel: == ist häufig eine bevorzugte Option beim Vergleichen von Objektreferenzen. In einigen Fällen scheint es jedoch auch so zu sein, dass Zeichenfolgenwerte verglichen werden.
Was Sie jedoch erhalten, ist ein fallender Wert, da Java die Illusion erzeugt, dass Sie Werte vergleichen, aber im eigentlichen Sinne nicht. Betrachten Sie die beiden folgenden Fälle:
Fall 1:
Fall 2:
Daher ist es viel besser, jeden Operator zu verwenden, wenn Sie das spezifische Attribut testen, für das er entwickelt wurde. In fast allen Fällen ist Objects.equals () ein universellerer Operator. Daher entscheiden sich erfahrene Webentwickler dafür.
Hier erhalten Sie weitere Informationen: http://fluentthemes.com/use-compare-strings-java/
quelle
Der richtige Weg wäre
quelle
Float.compare(1.1 + 2.2, 3.3) != 0
Eine Möglichkeit, Rundungsfehler zu reduzieren, besteht darin, Double anstelle von Float zu verwenden. Dadurch wird das Problem nicht behoben, aber die Fehlermenge in Ihrem Programm wird verringert, und Float ist fast nie die beste Wahl. MEINER BESCHEIDENEN MEINUNG NACH.
quelle