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.
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í:
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 ulp — roste s velikostí čísla. Relativní přesnost je pořád stejná, absolutní ne:
Praktické důsledky jsou tři:
- Celá čísla jsou ve
doublepřesná do 2⁵³ ≈ 9·10¹⁵. Nad tímx + 1 == x. Proto má JavaScriptNumber.MAX_SAFE_INTEGERa 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
floatneudě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.