Schreiben Sie in Java System.out.println(0.1 + 0.2); — statt 0.3 erscheint 0.30000000000000004. Das ist kein Java-spezifischer Fehler, sondern eine direkte Folge davon, wie der Standard IEEE 754 Bruchzahlen binär darstellt; dasselbe Ergebnis liefern JavaScript, Python, C# und praktisch jede andere Sprache, die auf diesem Standard basiert.
Die drei Teile einer Gleitkommazahl
Eine Zahl nach IEEE 754 wird als drei Komponenten gespeichert: ein Vorzeichen (positiv oder negativ), eine Mantisse (die signifikanten Ziffern der Zahl) und ein Exponent (die Potenz, um die das Komma verschoben wird). Das ähnelt der wissenschaftlichen Notation: 1.5 × 10², nur im Binärsystem.
Warum sich 0.1 nicht exakt binär darstellen lässt
0.1 im Dezimalsystem ist im Binärsystem ein unendlicher Bruch (ähnlich wie 1/3 im Dezimalsystem unendlich ist). Ein Computer speichert nur eine begrenzte Anzahl von Mantissenbits, daher wird die Zahl auf den nächstmöglichen darstellbaren Wert gerundet — und genau diese Rundung verursacht den sichtbaren „Fehler".
Wo das in der Praxis schadet: Rechnungen und Kassensysteme
Eine Kasse, die Artikelpreise als einfache Gleitkommazahlen aufaddiert, kann sich nach ausreichend vielen Posten um einen Cent vom erwarteten Gesamtbetrag verschieben — jede Addition rundet minimal, und die Fehler summieren sich, statt sich aufzuheben. Deshalb rechnen Buchhaltungs- und Kassensysteme meist mit ganzzahligen Cent-Beträgen oder einem dedizierten Decimal-Typ statt mit rohen float-Additionen.
Wofür man das braucht
- Unerwartete Rundungsfehler bei finanziellen oder wissenschaftlichen Berechnungen verstehen und diagnostizieren.
- Die exakte binäre Darstellung einer bestimmten Gleitkommazahl sehen.
- Einem Kollegen oder Studenten erklären, warum der Vergleich von Bruchzahlen auf exakte Gleichheit eine schlechte Praxis ist.
Subnormale Zahlen nahe null
Ist der Wert einer Zahl zu klein für die übliche Darstellung (eine Signifikand-Ziffer, die mit einer impliziten Eins beginnt), wechselt IEEE 754 in den subnormalen (denormalisierten) Modus, in dem diese implizite Eins entfällt — das erlaubt die Darstellung betragsmäßig noch kleinerer Zahlen um den Preis einer schrittweisen Präzisionseinbuße statt eines abrupten Sprungs auf null. Dieser „sanfte" Übergang zu null ist besser als ein plötzliches Nullwerden, aber subnormale Berechnungen sind auf manchen Prozessoren merklich langsamer als normale.