Vergleichen von Fließkommawerten unter Java

Einen Integerwert unter Java zu vergleichen ist trivial. Man nehme zwei Variablen vom Typ Integer und vergleiche sie, unter Zuhilfenahme des entsprechenden Operators, miteinander:

int a = 7;
int b = 7;

boolean equal = a == b;

Am Ende ist der Wert in der Variable equal true, da die Werte in der Variable a und b identisch sind. Nun könnte das Ganze bei Fließkommazahlen ebenso funktionieren:

double a = 7;
double b = 7;

boolean equal = a == b;

Auch in diesem Fall ist das Ergebnis am Ende true. Trotzdem sollten Fließkommazahlen niemals auf diese Weise verglichen werden. Spätestens bei folgendem Beispiel fällt dies auf:

double a = 4.1 + 0.8;
double b = 5.7 - 0.8;

boolean equal = a == b;

Obwohl beide Rechnungen eigentlich 5,9 ergeben sollten, wird in der Variable equal der Wert false enthalten sein. Hintergrund hierfür ist die Speicherung der Fließkommazahlen. Diese ist im Standard IEEE 754 festgelegt. Bei dieser Darstellung besteht die Zahl aus einem Vorzeichen, einem Exponent und einer Mantisse. Bei der Umwandlung der Dezimalzahl in ihre binäre Darstellung und der Verrechnung kann es zu Ungenauigkeiten (z.B. bei der Rundung im Prozessor) kommen. Wenn diese Werte nun binär verglichen werden, sind sie nicht identisch. Statt durch direkten Vergleich, sollte die “Gleichheit” durch eine Differenzrechnung ermittelt werden:

public static boolean compare(double a, double b) {
    return Math.abs(a - b) < 0.0001; // Diff smaller epsilon?
}

Nachdem die Differenz der beiden Werte gebildet wurde, wird überprüft ob diese kleiner als Epsilon sind. Epsilon stellt den Threshold bzw. eine Schwelle oder Toleranz dar. Solange die Differenz unter diesem Wert liegt, werden beide Werte als identisch angenommen. Natürlich fehlt in dieser Methode noch eine Absicherung gegenüber NaN-Werten.

Statt sich eine solche Methode selber zu schreiben, können Methoden aus bekannten Utility-Bibliotheken wie Guava (DoubleMath.fuzzyCompare) genutzt werden.

2 Byte Fließkommazahlen

4 Byte Fließkommazahlen kennt wahrscheinlich jeder Programmierer unter dem Namen “Single” oder “Float”. Die nächst größere Einheit sind die 8 Byte großen Fließkommazahlen welche meist den Namen “Double” tragen. Dann gibt es bei einigen Sprachen noch größere Fließkommazahlen Formate wie den “Decimal” unter C# und den “Extended” unter Pascal.

Den 2 Byte großen Fließkommatyp wie er in der IEEE 754 spezifiziert ist kennen allerdings nur die wenigsten. Er besteht dabei aus einem Bit als Vorzeichenbit, 5 Bit bilden den Exponent und die restlichen 10 Bit die Mantisse. Für C gibt es unter http://cellperformance-snippets.googlecode.com/files/half.c auch eine entsprechende Implementierung für einen solchen Half. Für C# ist eine Implementation unter http://csharp-half.svn.sourceforge.net/viewvc/csharp-half/ zu finden.

Interessant sind auch die Genauigkeitsprobleme eines solchen Halfs bei der Darstellung von Integerzahlen. So können die Zahlen 0 – 2048 exakt dargestellt werden, bei 2049 – 4096 werden die Zahlen auf ein Vielfaches von 2 gerundet und beim Wertebereich von 32769 bis 65519, wächst dieses schon auf ein Vielfaches von 32 an.

Weitere Informationen gibt es unter:
http://en.wikipedia.org/wiki/Half-precision_floating-point_format