von
gfoidl
Ich merke immer wieder dass vielen das Verständnis von Gleitkommazahlen fehlt und deshalb oft eine Vergleich mit == durchgeführt wird. Dies ist aber nicht korrekt.
Beispiel:
double d1 = 3.3;
double d2 = 1.1 + 2.2;
if (d1 == d2)
Console.WriteLine("gleich");
else
Console.WriteLine("ungleich");
Beim Ausführen des Code wird die korrekte Ausgabe “gleich” erwartet, es erscheint jedoch “ungleich”. Wie ist das möglich? Ist meine CPU defekt?
Die Begründung in diesem Verhalten liegt darin dass Gleitkommazahlen (float, double) im Rechner nur als Näherung einer reellen Zahl dargestellt werden. Es treten also Rundungsfehler auf. In der Numerik gibt es ganze Kapitel über die Behandlung von Fehlern und der Fortpflanzung. So weit gehe ich hier aber nicht.
Durchläuft man obigen Code mit dem Debugger und schaut sich die Werte von d1 und d2 an so kann festgehalten werden:
d1 3.3 double
d2 3.3000000000000003 double
Also existiert nur ein kleiner Unterschied in beiden Werten, der Vergleich mit == liefert somit korrekt das falsche Ergebnis.
Wie werden Gleitkommazahlen verglichen?
Es liegt somit Nahe einen Vergleich mit einer Toleranz durchzuführen. Dieses Toleranz wird in der Numerik üblicherweise als Epsilon bezeichnet.
Somit muss obiger Code geändert werden zu:
if (Math.Abs(d1 - d2) < epsilon)
Console.WriteLine("gleich");
else
Console.WriteLine("ungleich");
Wie groß soll Epsilon gewählt werden?
Die Datentypen System.Single (float) und System.Double (double) bietet eine Konstante namens Epsilon an. Diese entspricht aber nicht der Definition von Epsilon im Sinne der Numerik. Dieses Epsilon stellt die kleinste positive von 0 verschiedene Zahl dar.
Epsilon im Sinne der Numerik wird wie folgt berechnet:
public static double Epsilon()
{
double tau = 1.0;
double walt = 1.0;
double wneu = 0.0;
while (wneu != walt)
{
tau *= 0.5;
wneu = walt + tau;
}
return 2.0 * tau;
}
Es wird die Schleife so lange durchgeführt bis zwei Gleitkommazahlen als gleich erachtet werden. Dieses Epsilon (kann natürlich auch für float errechnet werden) sollte für Vergleiche von Gleitkommazahlen verwendet werden.
Epsilon für float: 1,192093E-07
Epsilon für double: 2,22044604925031E-16