ALIS

Software pro veřejnou správu

Vzdálená pomoc
13. 1. 2004

Nepřesné výpočty

Příklad problému:
V základní variantě runtime FANDu (bez koprocesoru), vyjde v kalkulačce (CtrlF5) následující výpočet takto:
484-487.6 = -3.6000000001.

Podobné problémy se mohou vyskytnout i v jiném kontextu, při výpočtech. Jde o starý problém, dokonce starší než FAND sám, který je ovšem v drtivé většině případů eliminován dalšími vlivy. Proto se s nim většina programátorů setká (pokud vůbec) až po delší praxi.

O co jde:
Číselné typy jsou ve FANDu (většinou obecně v počítači) ukládány v binární soustavě. Z čistě matematického hlediska téměř každé číslo, které má celkem jednoduchý dekadický (desetinný) rozvoj, třeba číslo 56.30, má v dvojkové soustavě nekonečný binární rozvoj.

Konkrétně, pokud počítáte nějaké korunové čátky třeba na desetihaléře, tak jedině částka x..x,50 Kč má konečný binární rozvoj, ale všechny ostatní "desetníky" jsou již vlastně uloženy nepřesně - x..x,10 atd mají nekonečný binární rozvoj.

To znamená, že pokud se k práci s čísly používá pohyblivá řádová čárka (typ real ve FANDu, potažmo v PASCALu, potažmo aritmetika pohyblivé řádové čárky v PC,...), nelze např. číslo 487.6 uložit přesně (v binární soustavě bude uloženo asi něco jako 487.599999999999) takže pak není divu, že ani zobrazený výsledek nemusí být přesný.

Takto napsáno ty vypadá přímo děsivě, ale praxe je jiná:

V první řadě je dobré si uvědomit, že pokud někde máme zobrazené nějaké číslo v dekadické soustavě, tak jde pouze a jenom o zobrazení hodnoty, která je v paměti počítači uložena většinou nějak jinak (binárně). Při zobrazení a konverzi čísla do dekadické soustavy dochází k celé řadě operací, včetně zaokrouhlení, takže výsledek se v naprosté většině případů zobrazí v očekávané podobě. Pokud nikoliv, tak lze použít formátování výsledku, např. WRITELN(xy:6:2).

Pokud nepracujete s příliš velkými čísly (co do celkového počtu míst), tak se tyto nepřesnosti eliminují. Pokud se vyskytnou problémy a nechcete dělat jiná opatření, stačí někdy přejít do verze pro coprocesor (ve verzi pro koprocesor je uvedený příklad O.K.).

O ošidnosti celé záležitosti však svědčí následující příklad výpočtu:?:
INT( 135.23 * 100)
který ve standardní verzi vyjde správně (13 523) ale ve verze pro koprocesor dá výsledek 13 522.

Co s tím:

  • Přesně z tohoto důvodu je ve FANDu zaveden typ F,m,n , kde se s číslem pracuje celočíselně. Má to však dost nepříjemných dopadů (v kalkulačce, musíte při výpočtech stále hlídat řád čísel apod) takže mnoho autorů raději používá základní typ F,m.n a hlídá si jen některá problémová místa, kde k těmto jevům dochází.
  • Zkuste např. výpočet(484-487.6) ROUND 2 = ...
    Takové zaokrouhlování se na řadě míst provádí automaticky, takže si uvedené problémy nikdo neuvědomuje. Viz. např. kapitola F, vypočtené údaje
    #C vypocet := neco1 - neco2 : F,6.2;
    obsahuje v sobě vlastně ROUND 2 (pro zobrazení v editoru).
  • V některých program. prostředcích (většinou v databázích) se používá např. typ MANY ( měna ), kde se počítá celočíselně jako u typu F,m.n, ale výpočty pak mohou být pomalejší.
  • Pozor také na to, že v souborech se pro uložení typu F,m.n nepoužívá formát pohyblivé řádové čárky ale číslo je uloženo "dekadicky" přesně. Problémy nastávají jen při výpočtech (číselné výrazy, typ real).
  • Velmi často se problémy vyskytují při použití funkce INT, která prostě "ořízne" desetinnou část čísla. V důsledku uvedených problémů pak mohou i celkem triviální výrazy dávat chybné výsledky. Viz. výše uvedený příklad. Běžnému uživateli při použití v kalkulačce to asi nemá smysl vysvětlovat, ale v programu lze vzorec "zabezpečit" malou úpravou
    INT(135.23*100+0.0000000001)
    T.j. před INT přičteme číslo za hranicí přesnosti použitých hodnot.