Dlaczego 0,1 + 0,2 nie daje 0,3

Norma IEEE 754 zapisuje liczbę jako znak, wykładnik i mantysę w systemie dwójkowym: wartość = (−1)^znak × 1,mantysa × 2^(wykładnik − przesunięcie). Pojedyncza precyzja ma 8 bitów wykładnika i 23 mantysy, podwójna 11 i 52. To format niemal wszystkich procesorów i każdego języka programowania.

W systemie dwójkowym skończony zapis mają tylko ułamki o mianowniku będącym potęgą 2. 0,5 i 0,25 tak, 0,1 nie: binarnie to okresowe 0,000110011001100…, które trzeba uciąć. Zapisywana jest najbliższa dostępna wartość, dla float 0,100000001490116…, a przy dodawaniu już przybliżonych wartości błędy się kumulują.

Dlatego liczb zmiennoprzecinkowych nie porównuje się przez ==, tylko sprawdza, czy różnica jest mała, a pieniądze liczy w całych groszach lub typach dziesiętnych. Format uzupełniają wartości specjalne: zero ze znakiem, nieskończoności, NaN i liczby zdenormalizowane, które wypełniają lukę przy zerze.

Częste błędy

  • Porównywanie dwóch liczb float przez ==: 0.1 + 0.2 == 0.3 jest fałszem w niemal każdym języku.
  • Używanie float do kwot pieniężnych: zaokrąglenia się sumują. Lepiej całe grosze albo typ dziesiętny.
  • Przekonanie, że 32-bitowy float ma 32 bity precyzji: mantysa ma 24, około 7 cyfr dziesiętnych; double ma około 16.

Najczęstsze pytania

Ile cyfr dziesiętnych mają float i double?

32-bitowy float gwarantuje około 7 cyfr znaczących, 64-bitowy double około 15–16. Dalsze cyfry odzwierciedlają już przybliżenie binarne.

Czym są liczby zdenormalizowane?

To wartości mniejsze od najmniejszej znormalizowanej: wykładnik składa się z samych zer, a mantysa traci ukrytą wiodącą jedynkę. Pozwalają stopniowo zbliżać się do zera kosztem precyzji.

Dlaczego istnieją +0 i −0?

Bo znak jest osobnym bitem. Oba zera są równe przy porównaniu, ale 1/+0 daje +∞, a 1/−0 daje −∞: zachowują stronę, z której dotarło się do zera.

Jak działa to obliczenie

Wartość = (−1)^s × 1,f × 2^(e − przesunięcie) dla liczb znormalizowanych, z przesunięciem 127 w pojedynczej i 1023 w podwójnej precyzji; dla zdenormalizowanych (e = 0) wartość to (−1)^s × 0,f × 2^(1 − przesunięcie). Wykładnik z samych jedynek z zerową mantysą to ±∞, z niezerową NaN. Dokładna wartość jest liczona dziesiętnie bez zaokrągleń: każdy float to m × 2^k, a dla ujemnego k to m × 5^(−k) ÷ 10^(−k).