BitLab vzorek, který se někdo zeptá

Tohle je statická kopie kapitoly pro vyhledávače. Interaktivní verze má animované obrázky, kontrolní otázky a tlačítka, která příklad načtou do kalkulačky.

Krok mřížky: proč 0,1 + 0,2 ≠ 0,3

Nejcitovanější podivnost v programování, a je to úplně obyčejná věc. 0,1 nejde ve dvojkové soustavě zapsat. Přesně tak, jako nejde v desítkové zapsat ⅓ — vyjde 0,333… a někde se to musí useknout.

0,1₁₀ = 0,0001100110011…₂periodické, jako ⅓ v desítkové

Uloží se tedy nejbližší číslo, které jde zapsat. To pro 0,1 i pro 0,2 i pro 0,3. Součet prvních dvou zaokrouhlení pak nemusí být totéž jako zaokrouhlení součtu — a taky není:

0,1 + 0,2 = 0,30000000000000004a rozdíl je přesně jeden krok mřížky u 0,3

Není to „zaokrouhlovací chyba“ v tom smyslu, že by šla zmenšit lepším kódem. Ta čísla nejsou uložená nepřesně náhodou — jsou uložená přesně jako nejbližší mřížkové body, a jejich součet leží mezi dvěma mřížkovými body. Proto se floaty neporovnávají na rovnost: místo a == b se ptá, jestli je |a − b| menší než smysluplná tolerance.

Mřížka není rovnoměrná

Tohle je ta část, kterou stojí za to si osahat. Krok mezi dvěma sousedními uložitelnými čísly — říká se mu ulproste s velikostí čísla. Relativní přesnost je pořád stejná, absolutní ne:

xsousední čísla, která JDOU uložitkolem hodnoty1,000e+0krok mřížky (ulp)2,220e-16relativně2,220e-16celá čísla ještě přesně?anoRelativní krok je pořád stejný. Absolutní roste s číslem — proto se velká a malá čísla nemají sčítat.
Táhni řádem a sleduj krok. U jedničky je krok 2⁻⁵², u 10¹⁶ už je krok 2 — a celá čísla přestanou být přesná. Přepni na binary32 a je to o osm řádů dřív.
ulp(x) ≈ x · 2−52binary64

Praktické důsledky jsou tři:

  • Celá čísla jsou ve double přesná do 2⁵³ ≈ 9·10¹⁵. Nad tím x + 1 == x. Proto má JavaScript Number.MAX_SAFE_INTEGER a proto se identifikátory z databáze nemají posílat jako číslo v JSONu.
  • Sčítat velké a malé je ztráta. Když je krok u velkého čísla větší než malé číslo celé, přičtení nezmění nic. Milion přičtení po 0,001 k hodnotě 10⁷ ve float neudělá nic.
  • Odečítat skoro stejná čísla je horší. Platné číslice se odečtou a zbude šum — katastrofické krácení. Proto se v FilterLabu kovariance nepočítá jako E[x²] − (E[x])².

Kdy tedy float ano

Když jde o měřenou fyzikální veličinu, která stejně má vlastní nejistotu větší, než je krok mřížky. Tam je plovoucí čárka přesně to pravé. Kdy ne: peníze, počty kusů, identifikátory a časová razítka — všechno, co je z podstaty celočíselné nebo přesné na haléř. Peníze se počítají v celých haléřích.

Vzorce v této kapitole

ulp — krok mřížky
ulp(x) ≈ x · 2⁻⁵² [[x]] kap. 9
2⁵³ — meze přesných celých čísel
|n| ≤ 2⁵³ ≈ 9·10¹⁵ [—] kap. 9