C++
Drijvende komma
Zoeken…
Zwevende puntnummers zijn raar
De eerste fout die bijna elke programmeur maakt, is ervan uitgaan dat deze code zal werken zoals bedoeld:
float total = 0;
for(float a = 0; a != 2; a += 0.01f) {
total += a;
}
De beginnende programmeur gaat ervan uit dat dit elk getal in het bereik 0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99
zal 0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99
om het resultaat 199
- het wiskundig correcte antwoord.
Er gebeuren twee dingen die dit onwaar maken:
- Het geschreven programma eindigt nooit.
a
nooit gelijk aan2
en de lus eindigt nooit. - Als we de luslogica herschrijven om in plaats daarvan
a < 2
te controleren, wordt de lus beëindigd, maar wordt het totaal iets anders dan199
. Op IEEE754-compatibele machines zal het in plaats daarvan vaak oplopen tot ongeveer201
.
De reden dat dit gebeurt is dat Floating Point Numbers benaderingen van hun toegewezen waarden vertegenwoordigen .
Het klassieke voorbeeld is de volgende berekening:
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;
Hoewel de programmeur drie getallen ziet die zijn geschreven in base10, zien de compiler (en de onderliggende hardware) binaire getallen. Omdat 0.1
, 0.2
en 0.3
een perfecte deling door 10
vereisen - wat vrij eenvoudig is in een base-10-systeem, maar onmogelijk in een base-2-systeem - moeten deze nummers worden opgeslagen in onnauwkeurige formaten, vergelijkbaar met hoe het nummer 1/3
moet worden opgeslagen in de onnauwkeurige vorm 0.333333333333333...
in base-10.
//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!