Dokumentace k projektu IZP č. 1



Projekt č. 1 - číselné soustavy
Autor: -tHE SWINe-

obsah:

Zadání úlohy

    Vytvořte program, který ze standardního vstupu načte celé kladné číslo v zadané číselné soustavě a na standardní výstup jej zapíše v jiné zadané soustavě. Uvažujte číselné soustavy od dvojkové do 36-kové. Číslice vyšších soustav než desítkové budou reprezentovány znaky 'A' až 'Z' (velká písmena).

    Program musí být schopen načíst maximální hodnotu datového typu unsigned long (konstanta ULONG_MAX z limits.h). Program musí být schopen detekovat chybové stavy ve vstupním řetězci.

    Je zakázáno používat knihovní funkce jazyka C, které problémy převodu mezi číselnými soustavami samy řeší (strtol(), ...). (Můžete je ovšem použít pro testování.)


Formáty dat

Formát vstupních dat
    Program se volá z příkazové řádky, možné parametry jsou buď "-h" pro zobrazení nápovědy, nebo "B1 B2", kde B1 a B2 jsou základy (base) číselných soustav. Zadané číslo(a) je v první soustavě, program ho vypíše v soustavě druhé.
    Čísla se zadávají do standardniho vstupu každé na jeden řádek, po každém čísle program vypíše konvertované číslo, odřádkuje a vytiskne oddělovač. Pokud jsou v čísle nepovolené znaky, program vypíše chybu. Program toleruje pro reprezentaci vysokých číslic čísla se základem vyšším, než deset písmena velké i malé latinské abecedy. Stejně tak vypisuje chyby i při příliš velkých číslech, která se nevejdou do typu unsigned long.
Formát výstupních dat
    Program vypisuje čísla bez počátečních nul, pokud je použita soustava vyššího řádu, než 10, použije pro výpis číslic velká písmena latinské abecedy. Výpis je proveden opět do standardního výstupu stdout.


Analýza problému

    Program bude číst kladná, celá čísla v zadané soustavě. Číselné soustavy se dělí na polyadické a nepolyadické (sem patří například Římská číselná soustava). My budeme používat jen polyadické soustavy, jinak by byl problém složitější. Napřed by asi bylo dobré si říct, na co soustavy jsou. Každá číselná soustava má nějaký svůj základ, který říká jak velké číslice bude číslo obsahovat. Dvojková soustava má základ dvě a může obsahovat pouze číslice 0 a 1. Desítková má základ 10 a obsahuje číslice 0 - 9. Šestnáctková (někdy také hexadecimální) má základ 16 a obsahuje číslice 0 - 15. Jak se takové číslice zapisují ? Jednoduše čísly 0 - 9 a když nestačí čísla, použijí se píemena latinské abecedy: A (=10) až F (=15). K čemu je to dobré ? Oproti nepolyadickým soustavám tady můžeme spočítat hodnotu čísla podle vzorce:

soustava_vzorec

Prozradíme si že a je ten základ a b je cifra na odpovídajícím místě v čísle. (cifra má tedy hodnoty od nuly do a - 1) Ukážeme si jednoduchý příklad, na kterém si ukážeme několik jednoduchých převodů:

různé_soustavy

    První číslo je v desítkové (to se píše do té závorky) soustavě. Je to číslo 123 (slovy stodvacettři). To se vyjádří jako 1 krát deset na druhou (jednička je na druhém místě) plus 2 krát deset na prvou plus 3 krát deset na nultou. Když to někam hodíte (do kalkulačky či sálového počítadla), vypadne vám výsledek: 123. (dříve či později) Pomocí takové metody se dají vyjádřit i desetinná čísla, i když se s nimi v praxi zrovna moc nepočítá (samozřejmě že počítá, ale většinou ne ručně).
    Na PC jsou pak desetinná vyjádřena jako exponent a mantissa, samozřejmě máme také znaménkový bit. Tohle řešení ale přináší malé úskalí: jedno číslo může být vyjádřeno více způsoby (podobně jako mohu napsat 1 = 10e-1 = 100e-2 ...). Proto občas nefunguje porovnávání čísel správně. Řešením je odečíst je a porovnat s velmi malým číslem (epsilon). Na počítačích jsou v současné době tři nejpoužívanější formáty desetinných čísel: double (64-bit, 11 bitů na exponent a 52 na mantissu + 1 znaménkový bit), float (32-bit, 8 bitů na exponent, 23 na mantissu a jeden zanaménkový). Ty jsou podle normy IEEE 754. Nedávno se k nim ale připojil ještě jeden formát, a to half (16-bit; 5 bitů na exponent a 10 na mantissu + 1 znaménkový), používaný na grafických kartách pro reprezentaci hodnot HDR.

Programové řešení

    Program bude postupovat tak, že přečte číslo ze standardního vstupu. Číslo se potom rozloží na jednotlivé číslice, kterým přísluší jejich hodnota podle vlastní číslice, pozice v číslice vzhledem k desetinné čárce a základ soustavy. V cyklu jednoduše předěláme číslo přímo z textu na unsigned long, jež bude naším interním formátem. Je třeba sledovat, jestli zadané číslo není moc velké a nedojde k přetečení. V assembleru by to bylo o něco jednodušší, hlídali bychom pouze stavový bit na procesoru (i když by nám přibyly zase jiné starosti). V C budeme hlídat, jestli výsledek přičtení každé další číslice k celkovému výsledku bude větší, než maximální číslo pro unsigned long. (232 - 1 = 4294967295).
    Při převodu čísla zpět využijeme jednoduchého postupu - budeme dělit číslo základem soustavy, ve kterém má být výsledek. Zbytek každého dělení odpovídá nejnižší číslici. Výsledek dělení je číslo bez této číslice a o řád menší. Tím docílíme rozkladu zpět na číslice, se kterým postupujeme dokud nám nezbude nula. Malý zádrhel je v tom, že čísla dostáváme v opačném pořadí, než bychom je chtěli. Dalo by se to řešit dvěma způsoby, a to počítat zbytky "z druhé strany čísla", což ale vyžaduje o řád vyšší mocninu základu, než je číslo (což by s velkými čísly dělalo problémy), takže jsem se rozhodl vyřešit problém pomocí pole do kterého číslice ukládám a potom je z pole vytisku v opačném pořadí.
nahoru