CodeGym /Kurse /JAVA 25 SELF /Genauigkeitsprobleme und Sonderwerte

Genauigkeitsprobleme und Sonderwerte

JAVA 25 SELF
Level 6 , Lektion 3
Verfügbar

1. Einleitung

Man könnte meinen: Wenn der Computer „klug“ ist, dann sollte 0.1 + 0.2 einfach 0.3 sein. Ganz so ist es jedoch nicht. Schauen wir uns ein einfaches Beispiel an.


double x = 0.1;
double y = 0.2;
double sum = x + y;
System.out.println(sum); // Was wird das Programm ausgeben?
Addition von Fließkommazahlen: Wir erwarten 0.3 – aber was kommt tatsächlich heraus?

Und nun versuchen Sie, mit 0.3 zu vergleichen:

System.out.println(sum == 0.3); // Wird das hier true oder false sein?

Wenn Sie false sehen, wundern Sie sich nicht!

Die Ursache liegt in der Darstellung von Zahlen im Speicher

Computer arbeiten mit Zahlen im Binärsystem. Doch längst nicht alle Dezimalbrüche lassen sich als endliche Binärbrüche darstellen – so wie sich 1/3 nicht exakt als Dezimalbruch schreiben lässt (0.333...). Zum Beispiel ist 0.1 im Binärsystem ein unendlicher Bruch und muss für die Speicherung „gerundet“ werden.

Vereinfacht gesagt speichert double manchmal scheinbar exakt, tatsächlich aber nur einen sehr nahen Näherungswert.

2. Welche „Macken“ treten bei der Arithmetik mit double auf?

Schauen wir uns praktische Beispiele an.

Beispiel 1. Die klassische „Magie“ 0.1 + 0.2

double a = 0.1;
double b = 0.2;
double sum = a + b;
System.out.println(sum);            // 0.30000000000000004
System.out.println(sum == 0.3);     // false

Der Rechner gab nicht 0.3 aus, sondern 0.30000000000000004. Der Unterschied ist klein, aber wenn Sie zum Beispiel mit Finanzen arbeiten – ist das bereits kritisch.

Beispiel 2. Iteratives Addieren

double result = 0;
for (int i = 0; i < 10; i++)
{
    result += 0.1;
}
System.out.println(result); // 0.9999999999999999

Gewünscht war 1.0 – heraus kommt etwas weniger. Wieder ist die Ursache die Rundung innerhalb von double.

Warum das in der Praxis wichtig ist

Viele denken: „Na und, ein kleiner Fehler, halb so wild!“ Betrachten wir ein Beispiel aus der Welt der Zahlungen.

Angenommen, Ihr Online-Banking addiert 100 Transaktionen je 0.1 Euro. Wenn Ihr Programm bei jeder Iteration um ein hunderttausendstel Euro „verliert“, verlieren Sie im Maßstab einer Bank echtes Geld. Da kommt die Buchhaltung sofort auf Sie zu und fragt: „Wo ist unser Geld?!“

3. Wie man Fließkommazahlen richtig vergleicht

Da double häufig nicht genau den Wert speichern kann, den Sie erwarten, kann der direkte Vergleich mit == in die Irre führen. Stattdessen vergleicht man üblicherweise den Betrag der Differenz mit einer sehr kleinen Zahl (Epsilon).

Beispiel: Vergleich mit Toleranz

double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;

if (Math.abs(a - b) < epsilon)
{
    System.out.println("Fast gleich!"); // So zu vergleichen ist sicherer
}

Hier sagen wir: „Wenn der Unterschied zwischen den Zahlen kleiner als ein Millionstel ist, betrachten wir sie als gleich.“
Hinweis: Die Funktion Math.abs(value) gibt den Absolutwert (Betrag) der übergebenen Zahl zurück.

4. Sonderwerte von double: Infinity, NaN, -Infinity

Der Typ double speichert nicht nur Zahlen, sondern auch besondere Werte. Diese entstehen in Situationen, die ein Mathematiker einer Studentin streng verboten hätte.

Unendlichkeit (Infinity)

Was passiert, wenn man 1 durch 0 teilt?

double result = 1.0 / 0.0;
System.out.println(result); // Infinity

In Java (und vielen anderen Sprachen) führt die Division durch 0 bei double nicht zu einer Exception! Stattdessen wird das Ergebnis zu einem speziellen Wert „positive Unendlichkeit“.

Minus-Unendlichkeit (-Infinity)

Teilt man eine negative Zahl durch 0, erhält man negative Unendlichkeit:

double result = -1.0 / 0.0;
System.out.println(result); // -Infinity

„Keine Zahl“ (NaN – Not a Number)

Wenn man etwas wirklich Ungewöhnliches tut, etwa die Quadratwurzel aus einer negativen Zahl berechnet:

double result = Math.sqrt(-1);
System.out.println(result); // NaN

Oder das Ergebnis der Division 0.0 / 0.0:

double result = 0.0 / 0.0;
System.out.println(result); // NaN

NaN ist gewissermaßen „alles, was im wirklichen Leben keine Zahl wäre“.

Prüfung auf Sonderwerte

In Java gibt es Funktionen zur Prüfung auf Sonderwerte:

System.out.println(Double.isInfinite(result));    // true, wenn Unendlichkeit
System.out.println(Double.isNaN(result));         // true, wenn NaN

Tabelle: Wie double auf ungewöhnliche Operationen reagiert

Operation Ergebnis Was in double gespeichert wird
1.0 / 0.0
Infinity +∞
-1.0 / 0.0
-Infinity -∞
0.0 / 0.0
NaN Keine Zahl
Math.sqrt(-1)
NaN Keine Zahl

5. Typische Fehler im Umgang mit Fließkommazahlen

Fehler Nr. 1: Vergleich von Gleitkommazahlen mit ==
Die häufigste Falle: zu prüfen, ob zwei berechnete Gleitkommazahlen mit dem normalen Vergleich gleich sind. Aufgrund von auflaufenden Rundungsfehlern erhalten Sie fast immer ein unerwartetes Ergebnis. Verwenden Sie immer einen Vergleich mit Toleranz (epsilon).

Fehler Nr. 2: Unerwartete NaN und Infinity in Berechnungen
Wenn Sie die Division durch Null oder Wurzeln aus negativen Zahlen nicht kontrollieren, können in Ihrem Programm NaN oder Infinity auftauchen und alle weiteren Berechnungen „anstecken“. Prüfen Sie verdächtige Werte mit Double.isNaN() und Double.isInfinite() – so vermeiden Sie böse Überraschungen.

Fehler Nr. 3: NaN als „Marker“ verwenden
Einige Einsteiger nutzen NaN als „Spezial“-Wert, etwa „falls nicht gefunden – geben wir NaN zurück“. Denken Sie daran: NaN ist tückisch, und der Vergleich mit == funktioniert nicht! Prüfen Sie ausschließlich mit den speziellen Methoden.

Fehler Nr. 4: Eine Exception beim Teilen durch 0.0 erwarten
Anders als bei Ganzzahlen wirft die Division durch 0.0 bei double keine Exception, sondern liefert Infinity oder NaN. Das kann zu leisen, schwer auffindbaren Bugs führen – ein Ergebnis ist da, aber nicht das erwartete.

1
Aufgabe
JAVA 25 SELF, Level 6, Lektion 3
Gesperrt
Überprüfung der Genauigkeit einer chemischen Lösung 🧪
Überprüfung der Genauigkeit einer chemischen Lösung 🧪
1
Aufgabe
JAVA 25 SELF, Level 6, Lektion 3
Gesperrt
Erforschung der Grenzen des Rechenuniversums 🌌
Erforschung der Grenzen des Rechenuniversums 🌌
1
Aufgabe
JAVA 25 SELF, Level 6, Lektion 3
Gesperrt
Versuch, das Unbekannte zu berechnen ❓
Versuch, das Unbekannte zu berechnen ❓
1
Aufgabe
JAVA 25 SELF, Level 6, Lektion 3
Gesperrt
Kalibrierung eines hochpräzisen Lasers 🔬
Kalibrierung eines hochpräzisen Lasers 🔬
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION