Zivanovy stránky - Vývoj - Problém reálných čísel na PC  

Altap Salamander
© Zivan

Problém reálných čísel na PC

Zřejmě každý začínající programátor na tento problém narazí, když hledá v chybu ve zdánlivě správné konstrukci.

Například tento jednoduše vypadající cyklus je nekonečný:
a = 0;
while (a != 1)
{
  a = a + 0,1;
}

Kdo se s tím ještě nesetkal, tak asi řekne "Sakra jak to? Desetkrát se k nule přičte 0,1 a musí to skončit." A ono ne.

Problém 1 - převod z desítkové do dvojkové soustavy

Pamět umí uchovat pouze 2 stavy (dvojková soustava), ale člověk používá desítkovou soustavu. Čísla se tedy musejí mezi soustavami převádět.

Převod celých čísel

Postup: Číslo v desítkové soustavě dělíme celočíselně dvojkou, zbytky po dělení píšeme postupně před sebe až do doby, kdy nám po celočíselném dělení zůstane nula.

Příklad:
11/2=5(zbytek 1)
5/2=2(zbytek 1)
2/2=1(zbytek 0)
1/2=0(zbytek 1)
V binárním tvaru se číslo 11 rovná číslu 1011. Používá se zápis (11)10 = (1011)2.

Při celočíselném počítání problém nenastává. Každé číslo se dá zapsat v konečném tvaru, v nejhorším je moc velké a program ohlásí chybu.

Převod reálných čísel

Postup: Celá část se převádí stejným způsobem jako celá čísla. Desetinná část se převádí tak, že ji postupně násobíme dvojkou, zapisujeme za sebe celou část a dál počítáme opět pouze s desetinnou, dokud nám nezůstane nula.

Příklad:
0,6875*2=1,375(celá část 1)
0,375*2=0,75(celá část 0)
0,75*2=1,5(celá část 1)
0,5*2=1,0(celá část 1)
Platí: (0,6875)10 = (0,1011)2

Převod reálný čísel vypadá taky bezproblémově.

Tak kde je problém?

Když si představíme graficky převod reálných čísel, tak se dostaneme k půlení intervalu. Desetinná část leží v intervalu od jedné do nuly, rozdělíme si tento interval na dvě poloviny (tzn. 0 - 0,5 a 0,5 - 1). Pokud je číslo v první polovině, tak si zapíšeme nulu, pokud v druhé tak jedničku. Takto můžeme pokračovat až do doby, než narazíme přesně na převáděné číslo (navigace po číselné ose). Toto hledání může být ale velmi dlouhé, někdy i nekonečné. Paměť počítače není nekonečná, takže tu "nepodstatnou" malou část na konci zahodíme.

A tady nastává problém. Uložené číslo není přesné a proto nám rovnost v úvodním cyklu nikdy neskončí. Řešením je nepoužívat rovnost, ale operátory větší než a menší než. V úvodním cyklu stačí změnit podmínku na "a >= 1".

Problém 2 - práce s velkým rozsahem mantisy

Pro zjednodušení budeme mít 8 místné kladné celé decimální číslo, kde prvních 6 míst budou platná číslice a 2 místa bude mantisa.

12345612
Používá se zápis 123456e12.



Příklady zápisu:
1e1=10
2e2=200
1e6=1 000 000
1e9=1 000 000 000

Z příkladů je vidět, že takhle můžeme jednoduše uložit dosti rozdílné hodnoty. Problém nastane u čísel, která mají více platných číslic. Např. číslo 123456789 ma 9 platných číslic, ale můžeme uložit pouze prvních 6, další číslice tedy zahodíme a uložené číslo bude 123456e2. Ve většině případů nám toto zaokrouhlení nevadí.

Co se ale stane, když k předchozímu číslu přičteme 1? Dostaneme číslo 12345601 a po uložení opět 123456e2. Když si pod tím představíme peníze - co to je 1 koruna při více než 12 miliónech? Nic, ale co když toto přičtení koruny provedeme miliónkrát? Najednou přijdeme o milión korun a to už taková sranda není, že? A stačilo by těch milión korun nejprve sečíst a až potom je přičítat.

V jazyku C jsou 3 typy reálných čísel. Do typu float se uloží 7 platných číslic, do typu double 15 a do long double 19 číslic. Použití přesnějšího čísla problém pouze odsune, ale nevyloučí chybu při nesprávném použití.


Test převodu soustav

Zde si můžete vyzkoušet převody číselných soustav. K funkci je potřeba zapnutý javascript. Hodnota chyba zaokrouhlení nemusí být přesná.

SoustavaCislo
Prevod z
Prevod do
Dalsi informace
Chyba zaokrouhleni (v desitkove):