C++
Fließkomma-Arithmetik
Suche…
Fließkommazahlen sind komisch
Der erste Fehler, den fast jeder Programmierer begeht, setzt voraus, dass dieser Code wie beabsichtigt funktioniert:
float total = 0;
for(float a = 0; a != 2; a += 0.01f) {
total += a;
}
Der Programmierer für Anfänger geht davon aus, dass dies jede einzelne Zahl im Bereich von 0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99
aufsummiert und das Ergebnis 199
ergibt - die mathematisch korrekte Antwort.
Zwei Dinge passieren, die dies unwahr machen:
- Das Programm, wie geschrieben, endet nie.
a
nie gleich2
und die Schleife endet nie. - Wenn wir die Schleifenlogik neu schreiben, um
a < 2
zu überprüfen, wird die Schleife jedoch beendet, aber die Summe ist etwas anders als199
. Auf IEEE754-kompatiblen Maschinen summiert sich diese Zahl häufig auf etwa201
.
Der Grund dafür ist, dass Gleitkommazahlen Näherungswerte ihrer zugewiesenen Werte darstellen .
Das klassische Beispiel ist die folgende Berechnung:
double a = 0.1;
double b = 0.2;
double c = 0.3;
if(a + b == c)
//This never prints on IEEE754-compliant machines
std::cout << "This Computer is Magic!" << std::endl;
else
std::cout << "This Computer is pretty normal, all things considered." << std::endl;
Der Programmierer sieht zwar drei in base10 geschriebene Zahlen, aber der Compiler (und die zugrunde liegende Hardware) sehen Binärzahlen. Da für 0.1
, 0.2
und 0.3
eine perfekte Division durch 10
erforderlich ist - was in einem Basis-10-System recht einfach, in einem Basis-2-System jedoch nicht möglich ist - müssen diese Zahlen in ungenauen Formaten gespeichert werden, ähnlich wie bei der Zahl 1/3
muss in der ungenauen Form 0.333333333333333...
in Base-10 gespeichert werden.
//64-bit floats have 53 digits of precision, including the whole-number-part.
double a = 0011111110111001100110011001100110011001100110011001100110011010; //imperfect representation of 0.1
double b = 0011111111001001100110011001100110011001100110011001100110011010; //imperfect representation of 0.2
double c = 0011111111010011001100110011001100110011001100110011001100110011; //imperfect representation of 0.3
double a + b = 0011111111010011001100110011001100110011001100110011001100110100; //Note that this is not quite equal to the "canonical" 0.3!