Rozdział 4 · Python lubi liczby

Dlaczego 0,1 + 0,2 nie jest dokładnie 0,3

Najbardziej znana „niespodzianka”

Jedno z najsłynniejszych „niespodzianek”

surpriz.py
print(0.1 + 0.2)
Rzeczywiste wyjście
0.30000000000000004
To nie jest błąd Python
Zobaczysz dokładnie takie samo zachowanie w JavaScript, Java, C C++ niemal każdym innym języku powszechnym. Powodem nie jest błąd Python, lecz sposób, w jaki KAŻDY komputer przechowuje liczby ułamkowe w binarnym.

Prosta analogia: 1/3 w zapisie dziesiętnym

W systemie dziesiętnym 1/3 nie można zapisać jako liczba skończona Tylko liczby 0.333333..., bez końca. Nie dlatego, że System dziesiętny jest „zły”

Komputer w systemie binarnym ma dokładnie ten sam problem, tylko z innymi liczbami. 0.1 w zapisie dziesiętnym wygląda „prosto” — ale w systemie binarnym zamienia się w nieskończenie powtarzającą się ułamek, dokładnie jak 1/3 w systemie dziesiętnym. Komputer jest zmuszony przyciąć go do skończonej liczby bitów — stąd ten mały błąd.

Dziesiętny 0,1to, co napisałaśNajbliżejbinarny…to, co jest prawdziwejest przechowywaneDrobny błądróżnica prawie żadnazauważalne
0.1 nie jest przechowywane równomiernie — przechowywane jest najbliższe możliwe przybliżenie binarne

Co dzieje się głębiej

Na większości nowoczesnych komputerów i w standardowym CPython float spełnia format sprzętowy o podwójnej precyzji, znany jako binary64 (część standardu IEEE 754). Koncepcyjnie liczba jest przechowywana w trzech częściach:

float (binary64)Znakplus lub minusWystawcaskala liczbyMantissaznaczące liczby
Z czego składa się typowy float w nowoczesnym CPython (standardowy binary64)
Nie trzeba się uczyć na pamięć
Dokładna liczba bitów na każdą część (1/11/52 w standardzie binary64) — szczegół, którego nie trzeba się uczyć od razu. Ważne jest zrozumienie samej zasady: wartość jest przechowywana przybliżona, a nie to, że Python „źle liczy”. To powszechne, dobrze zbadane zachowanie — standard binary64, a nie własna zasada języka Python.

Python może pokazać dokładną wartość zapisaną

Dobrą wiadomością jest to, że podejście nie jest przypadkowe ani tajemnicze – Python może Ci pokazać, Co dokładnie jest przechowywane:

tochnoe.py
print((0.1).as_integer_ratio())
# (3602879701896397, 36028797018963968) to dokładny ułamek, który faktycznie jest przechowywany
hex_predstavlenie.py
print((0.1).hex())
# '0x1.999999999999ap-4' to dokładna reprezentacja w formie szesnastkowej
Główne zakończenie sekcji
0,1 nie jest przechowywane „mniej więcej”
Praktyka: 0,1 + 0,2 — przeanalizuj przybliżenie
Interaktywny notatnik bezpośrednio w przeglądarce — as_integer_ratio(), hex() i przewidywanie wyniku
Otwórz praktykę →