Subsections

 
B. Artitmetika v plovoucí řádové čárce: Problémy a jejich náprava

V počítači jsou desettiná čísla reprezentována zlomky s čitatelem 2. Například desetinné číslo

0.125

má hodnotu 1/10 + 2/100 + 5/1000 stejně jako binární desetinné číslo

0.001

znamená 0/2 + 0/4 + 1/8. Tyto dvě čísla mají stejnou hodnotu s tím rozdílem, že první je zapsáno jako desítkové desetinné číslo a druhé jako binární desetinné číslo.

Bohužel, mnoho desetinných čísel zapsaných v desítkové soustavě nemá odpovídající binární tvar. Proto jsou desetinná čísla, která jste zadali, reprezentována pouze přibližnou hodnotou, která je uložena v počítači.

Tento problém nejsnadněji pochopíte na této ukázce: představte si zlomek 1/3. Můžete jej zapsat přibližne jako desetinné číslo:

0.3

nebo lépe:

0.33

nebo ještě lépe:

0.333

a tak dále. Nezáleží na tom, kolik číslic napíšete, výsledek nikdy nebude přesně 1/3, ale bude čím dál tím lepší aproximací zlomku 1/3.

Stejně tak nezáleží na tom, kolik binárních číslic použijete, dekadickou hodnotu 0.1 v binární soustavě nezapíšete. Ve dvojkové soustavě je 1/10 nekonečná posloupnost číslic

0.0001100110011001100110011001100110011001100110011...

Použitím konečného počtu bitů získáme pouze přibližnou hodnotu. Díky tomu můžete vidět výsledky podobné tomuto:

>>> 0.1
0.10000000000000001

Tento výsledek získáte, zadáte-li hodnotu 0.1 interaktivnímu interpretru. Někdy můžete dostat jiný výsledek, protože počet bitů použitý k uchování desetinných čísel se liší mezi různými platformami. Python vždy vytiskne pouze hodnotu přibližného binárního čísla uchovaného v počítači. Na některých počítačích byste mohli jako binární aproximaci čísla 0.1 získat

>>> 0.1
0.1000000000000000055511151231257827021181583404541015625

Interaktivní interpretr Pythonu pro získání řetězcové reprezentace hodnot implicitně používá interní funkci repr(). Pro desetinná čísla repr(float) zaokrouhlí desetinnou hodnotu na 17 platných cifer. 17 cifer bylo použito, aby pro všechna desetinná čísla byl splněn výraz eval(repr(x)) == x. 16 cifer by již bylo nedostačujících.

Pamatujte, že tento problém souvisí s reprezentací binárních čísel v plovoucí řádové čárkce, nejde o chybu v Pythonu, také to není chyba vašeho kódu, se stejnými problémy se setkáte i v jiných jazycích, které pro práci s desetinnými čísly používají numerický koprocesor (nebo jiný hardware), přestože některé jazyky rozdíly implicitně zobrazovat nemusí.

Interní funkce str() vrací pouze 12 platných číslic. Můžete ji tedy používat místo repr(). Kód eval(str(x)) nelze použít k reprodukci x, ale její výstup může vypadat lépe:

>>> print str(0.1)
0.1

Musíte však pamatovat na to, že to je pouze iluze: hodnota ve skutečnosti není přesně 1/10, my jsme pouze zaokrouhlili její hodnotu na 12 platných číslic.

Můžete se dočkat i dalších překvapení. Například, uvidíte-li

>>> 0.1
0.10000000000000001

můžete se pokusit použít funkci round() pro zaokrouhlení na tolik číslic, kolik si přejete. Zde však neuvidíte žádný rozdíl:

>>> round(0.1, 1)
0.10000000000000001

Problém je v tom, že binární desetinné číslo uložené jako hodnota 0.1 již je nejlepší možnou aproximací zlomku 1/10, takže pokusíte-li se jí zaoukrouhlit, nezískáte již lepší výsledek.

Jiné překvapení -- 0.1 není přesně 1/10, takže sečtete-li desetkrát číslo 0.1, nedostanete 1.0:

>>> sum = 0.0
>>> for i in range(10):
...     sum += 0.1
...
>>> sum
0.99999999999999989

Binární reprezentace desetinných čísel přináší ještě další podobné problémy. Problém 0.1 je více popsán níže v sekci "Chyby v reprezentaci čísel". Také se můžete podívat na dokument The Perils of Floating Point, kde najdete kompletní výčet všech možných "chyb".

Jednoduchá odpověď neexistuje. Aritmetiky v plovoucí řádové čárce se ale bát nemusíte! Chyby v operacích v plovoucí řádové čárce jsou závislé na hardware a na většině počítačů dojde řádově k jedné chybě na 2**53 operací. To je více než postačující pro většinu úloh. Vždy ale musíte myslet na to, že Python nepoužívá desítkovou ale binární aritmetiku a každá operace v plovoucí desetinné čárce může zavést další chybu.

Očekávaný výsledek můžete získat použitím funkce str() a pro ještě lepší kontrolu nad formátem desetinného čísla můžete použít operátor % a formátovací řetězce %g, %f a %e.

 
B.1 Chyby v reprezentaci čísel

V této sekci se podíváme na "0.1" více do hloubky. Ukážeme si jak můžete sami najít problémy podobné tomuto. Předpokládám základní znalosti o reprezentaci binárních desetinných šísel.

Chyba v reprezentaci čísel znamená, že některá desetinná čísla v desítkové soustavě přesně neodpovídají binárním desetinným číslům. To je jediný důvod proč Python (nebo Perl, C, C++, Java, Fortran a další jazyky) často nezobrazí přesně to číslo, které jste očekávali:

>>> 0.1
0.10000000000000001

Proč? 1/10 nemá odpovídající zápis v binárním tvaru. Téměř všechny dnešní počítače (listopad 2000) používají aritmetiku v plovoucí řádové čárce tak, jak to předepisuje standard IEEE-754 a téměř na všech platformách odpovídá Pythonovský typ float číslu s dvojitou přesností podle IEEE-754. Tato čísla obsahují 53 bitů přesnosti, takže při zadání hodnot počítač převede 0.1 na nejbližší zlomek ve tvaru J/2**N, kde J je celé číslo obsahující přesně 53 bitů. Přepíšeme-li

 1 / 10 ~= J / (2**N)

jako

J ~= 2**N / 10

a za předpokladu, že J má přesně 53 bitů (je >= 2**53 ale < 2**53), je nejlepší hodnotou pro N 56.

>>> 2L**52
4503599627370496L
>>> 2L**53
9007199254740992L
>>> 2L**56/10
7205759403792793L

56 je jediná hodnota N, která zajistí, že J je dlouhá 53 bitů. Nejlepší možnou hodnotu pro J získáme po zaokrouhlení:

>>> q, r = divmod(2L**56, 10)
>>> r
6L

>>> q, r = divmod(2L**56, 10)
>>> r
6L

Jelikož je zbytek větší než polovina z 10, nejlepší aproximaci získáme zaokrouhlením nahoru:

>>> q+1
7205759403792794L

Takže nejlepší možnou reprezentací 1/10 v IEEE-754 typu double precision je tato hodnota vydělená 2**56, tedy

7205759403792794 / 72057594037927936

Protože jsme zaokrouhlovali nahoru, je výsledek o trochu větší než 1/10. Pokud bychom zaokrouhlení neprovedli, výsledek by byl trochu menší než 1/10. Ale nikdy nebude přesně 1/10!

Takže počítače nikdy "neuvidí" 1/10: to co vidí je zlomek tak jak jsme jej viděli výše:

>>> .1 * 2L**56
7205759403792794.0

Vynásobíme-li tuto hodnotu číslem 10**30, uvidíme hodnotu jejích 30 nejvýznamnějších dekadických číslic:

>>> 7205759403792794L * 10L**30 / 2L**56
100000000000000005551115123125L

Číslo uložené v počítači je tedy přibližně rovno dekadickému číslu 0.100000000000000005551115123125. Zaokrouhlením na 17 platných číslic získáme 0.10000000000000001, které Python zobrazí (dobrá, zobrazí jen na platformě podporující standard IEEE-754 a která provádí konverzi na nejlepší možnou hodnotu při zadávání a zobrazování čísel v C knihovně -- vaše platforma tak činit nemusí!).

See About this document... for information on suggesting changes.