Gleitkomma in Java manipulieren und vergleichen

8

In Java wird die Gleitkommaarithmetik nicht genau dargestellt. Zum Beispiel dieser Java-Code:

%Vor%

Druckt "c ist nicht 3.6".

Ich bin nicht an einer Genauigkeit von mehr als 3 Dezimalstellen (#. ###) interessiert. Wie kann ich mit diesem Problem umgehen, um Floats zu multiplizieren und sie zuverlässig zu vergleichen?

    
Praneeth 24.05.2010, 09:42
quelle

8 Antworten

16

Es ist eine allgemeine Regel, dass Gleitkommazahlen niemals wie (a == b) verglichen werden sollten, sondern eher wie (Math.abs(a-b) < delta) , wobei delta eine kleine Zahl ist.

Ein Fließkommawert mit einer festen Anzahl von Ziffern in dezimaler Form muss nicht unbedingt eine feste Anzahl von Ziffern in binärer Form haben.

Zusatz für die Klarheit:

Obwohl der strikte == Vergleich von Fließkommazahlen sehr wenig praktischen Sinn hat, ist der strikte < und > Vergleich dagegen ein gültiger Use Case (Beispiel - Logic triggering wenn ein bestimmter Wert den Schwellenwert überschreitet: (val > threshold) && panic(); )

    
bobah 24.05.2010 09:51
quelle
6

Wenn Sie an festen Präzisionszahlen interessiert sind, sollten Sie einen festen Genauigkeitstyp wie BigDecimal verwenden, keinen inhärent ungefähren (wenn auch hochpräzisen) Typ wie float . Es gibt zahlreiche ähnliche Fragen zu Stack Overflow, die in vielen Sprachen ausführlicher behandelt werden.

    
David M 24.05.2010 09:43
quelle
4

Ich denke, es hat nichts mit Java zu tun, es passiert auf jeder IEEE 754 Fließkommazahl. Dies liegt an der Natur der Gleitkommadarstellung. Alle Sprachen, die das IEEE 754-Format verwenden, stoßen auf das gleiche Problem.

Wie von David oben vorgeschlagen, sollten Sie die Methode abs der Klasse java.lang.Math verwenden, um den absoluten Wert zu erhalten (das positive / negative Vorzeichen löschen).

Sie können dies lesen: Ссылка und auch ein gutes Lehrbuch für numerische Methoden wird das Problem ausreichend lösen.

> %Vor%     
Daniel Baktiar 24.05.2010 10:11
quelle
2

Dies ist eine Schwäche aller Fließkommadarstellungen, und dies geschieht, weil einige Zahlen, die im Dezimalsystem eine feste Anzahl von Dezimalzahlen zu haben scheinen, im Binärsystem tatsächlich eine unendliche Anzahl von Dezimalzahlen haben. Und was Sie denken ist 1.2 ist eigentlich etwas wie 1.1999999999997, denn wenn es in Binärdarstellung darstellt, muss es die Dezimalzahlen nach einer bestimmten Zahl abschneiden, und Sie verlieren etwas Präzision. Wenn man es dann mit 3 multipliziert, ergibt das 3.5999999 ...

Ссылка & lt; - dies könnte es besser erklären (auch wenn es für Python ist, ist es eine häufige Problem der Gleitkommadarstellung)

    
Andrei Fierbinteanu 24.05.2010 10:14
quelle
2

Wie die anderen geschrieben haben:

  

Vergleichen floats mit: if (Math.abs(a - b) < delta)

Sie können eine schöne Methode dafür schreiben:

%Vor%

So können Sie es wie folgt verwenden:

%Vor%     
Martijn Courteaux 24.05.2010 10:46
quelle
2

Ich benutze dieses Codebeispiel in Komponententests, um zu vergleichen, ob das Ergebnis von zwei verschiedenen Berechnungen dasselbe ist, wenn Fließkomma-Rechenfehler ausgeschlossen werden.

Es funktioniert, indem man sich die binäre Darstellung der Fließkommazahl anschaut. Der größte Teil der Komplikation beruht auf der Tatsache, dass das Vorzeichen von Fließkommazahlen kein Zweierkomplement ist. Nach dem Ausgleich kommt es im Grunde nur auf eine einfache Subtraktion an, um den Unterschied in ULPs zu erhalten (erklärt in dem Kommentar unten).

%Vor%

Hier ist eine Version für double precision floats:

%Vor%     
Laurens Holst 26.07.2013 01:20
quelle
2

Es gibt eine Apache-Klasse zum Vergleichen von Doppelpunkten: org.apache.commons.math3.util.Precision

Es enthält einige interessante Konstanten: SAFE_MIN und EPSILON , die die maximal möglichen Abweichungen bei arithmetischen Operationen sind.

Es stellt auch die notwendigen Methoden zur Verfügung, um doppelte, gleiche oder runde zu vergleichen.

    
bvdb 19.05.2015 11:24
quelle
0

Um zwei Gleitkommazahlen zu vergleichen, f1 und f2 innerhalb der Genauigkeit von #.### Ich glaube, Sie müssten das folgendermaßen machen:

%Vor%

f1 * 1000 lifts 3.14159265... bis 3141.59265 , + 0.5 Ergebnisse in 3142.09265 und% (int) löscht die Dezimalstellen, 3142 . Das heißt, es enthält 3 Dezimalstellen und rundet die letzte Ziffer richtig ab.

    
aioobe 24.05.2010 10:12
quelle