Ułamki w pułapce
🎯 Po co Ci to?
Wpisz w konsoli Pythona 0.1 + 0.2 i naciśnij Enter. Zobaczysz 0.30000000000000004. To nie awaria Twojego komputera — ten sam wynik da każdy komputer świata, od smartwatcha po superkomputer. Maszyna, która bezbłędnie mnoży czterdziestocyfrowe liczby, „nie umie" dodać jednej dziesiątej do dwóch dziesiątych? Umie — tyle że jedna dziesiąta nie istnieje w jej świecie. Dziś zajrzysz do świata liczb zmiennoprzecinkowych: dowiesz się, co komputer naprawdę przechowuje, gdy piszesz 0.1, i dlaczego inżynierowie od rakiet i banków traktują ten temat śmiertelnie poważnie.
✅ Czego się nauczysz
Po tej jednostce potrafisz:
- wyjaśnić, jak działa zapis zmiennoprzecinkowy (mantysa i wykładnik);
- wskazać, których ułamków nie da się zapisać dokładnie dwójkowo i dlaczego;
- rozróżnić błąd przybliżenia i błąd zaokrąglenia oraz podać skutki ich kumulacji.
📘 Wyjaśnienie
Ułamki dwójkowe. Pozycje po przecinku też są potęgami podstawy — tyle że ujemnymi. W systemie dziesiętnym: dziesiąte, setne, tysięczne. W dwójkowym: połówki, ćwiartki, ósemki:
$$0{,}101_2 = \tfrac{1}{2} + \tfrac{1}{8} = 0{,}625_{10}$$
I tu niespodzianka. Zapisz jedną dziesiątą dwójkowo: $\tfrac{1}{10}$ nie jest sumą żadnego skończonego zestawu połówek, ćwiartek, ósemek… Rozwinięcie wychodzi okresowe: $0{,}1_{10} = 0{,}0001100110011\dots_2$ — dokładnie tak, jak $\tfrac{1}{3}$ w systemie dziesiętnym daje $0{,}333\dots$ To nie wada komputera, tylko arytmetyka: skończenie zapisują się w podstawie 2 wyłącznie ułamki o mianowniku będącym potęgą dwójki. $\tfrac{1}{2}$, $\tfrac{3}{8}$ — tak; $\tfrac{1}{10}$, $\tfrac{1}{3}$ — nie.
Zapis zmiennoprzecinkowy. Komputer ma na liczbę skończoną liczbę bitów, a musi obsłużyć i masę elektronu, i odległość do Andromedy. Ratunek znasz z fizyki — notacja wykładnicza: $6{,}02 \cdot 10^{23}$. Komputer robi to samo w podstawie 2:
📐 DEFINICJA — liczba zmiennoprzecinkowa: liczba zapisana w postaci $m \cdot 2^{w}$, gdzie mantysa $m$ przechowuje cyfry znaczące (na ustalonej liczbie bitów), a wykładnik $w$ — rząd wielkości. Standardowo (64 bity): 1 bit znaku, 11 bitów wykładnika, 52 bity mantysy.
Po ludzku: notacja naukowa z fizyki, tylko dwójkowa i z twardym limitem miejsca na cyfry. Czym NIE jest: dokładnym zapisem dowolnej liczby. To zapis „najbliższej liczby, jaką stać nasz budżet bitów".
Skutek limitu mantysy: gdy piszesz 0.1, komputer zapisuje najbliższą reprezentowalną liczbę — coś w rodzaju $0{,}100000000000000005551\dots$ To jest błąd przybliżenia: różnica między liczbą prawdziwą a jej reprezentacją. Gdy potem liczysz, wyniki są dodatkowo przycinane do dostępnych bitów — każdy taki ubytek to błąd zaokrąglenia. Pojedynczo są śmiesznie małe ($10^{-17}$!). Groźne robią się, gdy się kumulują — na przykład w pętli wykonanej miliard razy — albo gdy odejmujesz od siebie dwie prawie równe liczby i z cyfr znaczących zostaje szum.
🕰️ Skąd to wiemy: 25 lutego 1991 roku bateria antyrakietowa Patriot w Zahranie nie przechwyciła rakiety Scud — zginęło 28 żołnierzy. Śledztwo wykazało: system mierzył czas w dziesiątych sekundy, a $0{,}1$ s zapisywał w 24 bitach z błędem ok. $10^{-7}$. Po stu godzinach nieprzerwanej pracy uzbierało się 0,34 sekundy — przy celu lecącym 1,7 km/s to ponad pół kilometra pomyłki. Miliardowe części, pomnożone przez miliony taktów, potrafią zabić. Od tamtej pory „kumulacja błędu zaokrąglenia" jest w każdym podręczniku inżynierii.
Jak z tym żyć? Trzy zawodowe nawyki. Po pierwsze: nie porównuj ułamków przez „równa się" — zamiast x == 0.3 sprawdzaj, czy $|x - 0{,}3|$ jest mniejsze od małego progu (nazywanego epsilonem). Po drugie: pieniądze licz w groszach jako liczbach całkowitych — całkowite są dokładne; banki tak właśnie robią. Po trzecie: unikaj odejmowania bliskich sobie liczb, gdy możesz przekształcić wzór.
🧮 Prześledź
Rozstrzygnij bez komputera, które z liczb mają skończony zapis dwójkowy: $0{,}5$; $0{,}75$; $0{,}2$; $0{,}375$; $0{,}3$.
Sprawdź odpowiedź
$0{,}5 = \tfrac12$ ✓; $0{,}75 = \tfrac34 = \tfrac12+\tfrac14$ ✓; $0{,}2 = \tfrac15$ — mianownik 5, nie potęga dwójki ✗; $0{,}375 = \tfrac38$ ✓; $0{,}3 = \tfrac{3}{10}$ ✗. Reguła praktyczna: skróć ułamek maksymalnie i spójrz na mianownik — tylko czyste potęgi dwójki (1, 2, 4, 8, 16…) dają zapis skończony.
⚠️ Uwaga, pułapka
Wynik 0.30000000000000004 nie oznacza, że „Python źle liczy". Python policzył idealnie — na tych liczbach, które naprawdę dostał (czyli reprezentacjach $0{,}1$ i $0{,}2$). Błąd wszedł do gry w chwili zapisu danych, zanim padło jakiekolwiek dodawanie. Rozróżnienie „błąd danych" i „błąd obliczeń" to połowa maturalnych pytań z tego tematu.
🛠️ Teraz Ty
Z komputerem: sprawdź w Pythonie 0.1 + 0.2 == 0.3 (wynik Cię nie zdziwi), potem abs(0.1 + 0.2 - 0.3) < 1e-9. Zsumuj 0.1 dziesięć tysięcy razy w pętli i porównaj z 1000 — o ile się różni? Bez komputera: wyjaśnij w trzech zdaniach koledze, dlaczego sklep internetowy trzyma ceny w groszach.
📐 Definicje tej lekcji
- Mantysa i wykładnik — cyfry znaczące i rząd wielkości w zapisie $m \cdot 2^w$.
- Błąd przybliżenia — różnica między wartością prawdziwą a jej reprezentacją (powstaje przy zapisie danych).
- Błąd zaokrąglenia — ubytek dokładności przy przycinaniu wyniku działania; kumuluje się w długich obliczeniach.
📌 Najważniejsze w pigułce
- Skończenie zapisują się dwójkowo tylko ułamki o mianowniku-potędze dwójki; $0{,}1$ do nich nie należy.
- Zmiennoprzecinkowy = notacja naukowa z limitem bitów; przechowywana jest najbliższa reprezentowalna liczba.
- Ułamków nie porównujemy przez
==; pieniądze liczymy w całkowitych groszach; błędy drobne + miliardy operacji = błąd duży.
🎒 Zadania
- Zamień na system dziesiętny: $0{,}11_2$, $0{,}0101_2$, $101{,}101_2$.
Wskazówka i odpowiedź
$0{,}11_2 = \tfrac12+\tfrac14 = 0{,}75$. $0{,}0101_2 = \tfrac14 + \tfrac1{16} = 0{,}3125$. $101{,}101_2 = 5 + 0{,}625 = 5{,}625$. Część całkowita i ułamkowa liczą się niezależnie — przecinek tylko rozdziela dodatnie potęgi od ujemnych.
- Program bankowy nalicza klientowi codziennie 0,1% odsetek, mnożąc saldo przez
1.001w liczbach zmiennoprzecinkowych, przez 30 lat. Wskaż dwa powody, dla których prawdziwy bank tak nie zrobi.
Wskazówka i odpowiedź
(1) Kumulacja błędów zaokrągleń: ~11 tysięcy mnożeń na niedokładnych reprezentacjach da saldo różniące się od należnego — a przy milionach kont różnice urosną do realnych kwot i sporów prawnych. (2) Wymóg dokładności co do grosza: księgowość musi się bilansować idealnie, więc kwoty trzyma się w całkowitych groszach, a zaokrąglenia wykonuje jawnie, według przepisów (i zapisuje, dokąd poszły ułamki grosza).
- Dlaczego błąd rośnie szczególnie przy odejmowaniu bliskich sobie liczb? Przeanalizuj przykład: $x = 1{,}23456789$ i $y = 1{,}23456700$, obie znane z dokładnością do 8 cyfr znaczących.
Wskazówka i odpowiedź
$x - y = 0{,}00000089$. Z ośmiu cyfr znaczących każdej liczby w wyniku zostały dwie — reszta się skróciła. Jeśli ostatnia cyfra każdej z liczb była niepewna (a była — to limit reprezentacji), to niepewność, dotąd śladowa, dotyczy teraz drugiej cyfry wyniku: względny błąd urósł miliony razy. Zjawisko nazywa się utratą cyfr znaczących i to przez nie przekształca się wzory, zanim się je policzy.
🔍 Sprawdź, czy umiesz
- Wyjaśnić wynik
0.30000000000000004tak, żeby zrozumiał go humanista. - Rozróżnić błąd przybliżenia od błędu zaokrąglenia na przykładzie.
- Podać dwie zawodowe zasady obchodzenia się z ułamkami w programach.