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:

  1. Das Programm, wie geschrieben, endet nie. a nie gleich 2 und die Schleife endet nie.
  2. Wenn wir die Schleifenlogik neu schreiben, um a < 2 zu überprüfen, wird die Schleife jedoch beendet, aber die Summe ist etwas anders als 199 . Auf IEEE754-kompatiblen Maschinen summiert sich diese Zahl häufig auf etwa 201 .

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!


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow