Szukaj…


Liczby zmiennoprzecinkowe są dziwne

Pierwszym błędem, jaki popełnia prawie każdy programista, jest założenie, że ten kod będzie działał zgodnie z przeznaczeniem:

float total = 0;
for(float a = 0; a != 2; a += 0.01f) {
    total += a;
}

Początkujący programista zakłada, że zsumuje każdą pojedynczą liczbę w zakresie 0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99 , aby uzyskać wynik 199 - matematycznie poprawną odpowiedź.

Zdarzają się dwie rzeczy, które czynią to nieprawdziwym:

  1. Program w formie pisemnej nigdy się nie kończy. nigdy nie staje się równa 2 , a pętla nigdy nie kończy.
  2. Jeśli zamiast tego przepisujemy logikę pętli, aby sprawdzić a < 2 , pętla kończy się, ale suma kończy się na coś innego niż 199 . Na maszynach zgodnych z IEEE754 często sumuje się do około 201 .

Dzieje się tak dlatego, że liczby zmiennoprzecinkowe reprezentują aproksymacje przypisanych im wartości .

Klasycznym przykładem jest następujące obliczenie:

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;

Chociaż my, programista, widzimy trzy liczby zapisane w bazie 10, to, co widzi kompilator (i podstawowy sprzęt), są liczbami binarnymi. Ponieważ 0.1 , 0.2 i 0.3 wymagają idealnego dzielenia przez 10 - co jest dość łatwe w systemie base-10, ale niemożliwe w systemie base-2 - liczby te muszą być przechowywane w nieprecyzyjnych formatach, podobnie jak liczba 1/3 musi być przechowywany w nieprecyzyjnej formie 0.333333333333333... w bazie-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!


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow