Formáty čísel s pohyblivou řádovou čárkou
V článku si ukážeme formáty čísel s plovoucí čárkou v různých počítačích a kalkulačkách - Busicom 141-PF, TI-59, ZX Spectrum, IEEE754 a jiné.
Nejdříve se zastavíme u jedné z prvních stolních kalkulaček, Busicom 141-PF s procesorem Intel 4004, z roku 1971. Kalkulátor sice ještě nepoužívá exponent, ale obsahuje informaci o pozici desetinné tečky, tedy první náznak exponentu. Příklad zobrazení čísla -75.43:
Registr čísla obsahuje 20 BCD číslic (tj. buňky s hodnotou 0 až 15). Prvních 16 číslic je mantisa, uloženo od nejnižší číslice k nejvyšší, poslední 4 číslice jsou stavové buňky. M0 se nachází na nejnižší adrese paměti RAM, S3 na nejvyšší. Buňka S0 obsahuje flagy: 0 je pozitivní číslo, 15 negativní. Buňka S1 obsahuje pozici desetinné tečky, tedy počet číslic za desetinnou tečkou. Online emulátor kalkulátoru: https://dutchen18.gitlab.io/emu-rs/ .
V roce 1973 vydala firma Texas Instruments svou kalkulačku Datamath TI-2500 s procesorem TMS0800. Na rozdíl od procesoru Intel, tento procesor byl zaměřený speciálně na kalkulačky a prováděl operace s celým číslem jednou instrukcí, s použitím masky určující, se kterými číslicemi se operace provede. Příklad zobrazení čísla -75.43:
Registr čísla obsahuje 11 BCD číslic. Buňka S10 obsahuje znaménko: 0 kladné, 14 záporné. Následuje 8 číslic mantisy, v S9 nejvyšší číslice, v S2 nejnižší číslice. S1 a S0 obsahují pozici desetinné tečky - tj. počet číslic za desetinnou tečkou. Při zobrazení se číslo spolu se znaménkem zarovná doprava, tj. vypustí se nadbytečné nuly za desetinou tečkou. Potlačení nevýznamných nul před číslem zajistí hardware. Během výpočtů se namísto znaménka použije 9. číslice.
Online emulátor kalkulátoru spolu s podrobným popisem: http://files.righto.com/calculator/TI_calculator_simulator.html .
Clive Sinclair měl představu prodávat levný kalkulátor, který by uměl vědeckotechnické výpočty. Chtěl k tomu použít procesor TMS0800 (použitý u předešlé kalkulačky Datamath), protože ho od firmy Texas Instruments mohl dostat levně. Inženýři z Texas Instruments se mu vysmáli, že je to nemožné, procesor nemá takové vybavení a má k dispozici prostor jen pro 320 instrukcí. Stalo se neuvěřitelné, Sinclair to dokázal a v roce 1974 začal prodávat levný kalkulátor, který uměl sinus, kosinus, tangens, arkus sinus, arkus cosinus, arkus tangens, logaritmus a exponenciál. Příklad zobrazení čísla -75.43:
Pro lepší optimalizaci kódu jsou čísla v registrech uspořádána jinak než u Datamathu. V první buňce, S10, je znaménko mantisy. 0 znamená nezáporné číslo, 5 záporné. V druhé buňce, S9, je znaménko exponentu. Další 2 číslice, S8 a S7, jsou exponent. Následuje mantisa. Během výpočtů se používá mantisa 6 číslic a začíná číslicí S6. Při zobrazení má mantisa 5 číslic a začíná na pozici S5. Kalkulátor zobrazuje čísla vždy ve tvaru: znaménko mantisy, 5 číslic mantisy (s desetinou tečkou za první číslicí), znaménko exponentu a 2 číslice epxonentu.
Sinclair Scientific se stal prvním levným vědeckým kalkulátorem postaveném na jediném čipu. Přesto se velkého úspěchu nedočkal a kvůli poklesu cen konkurenčních kalkulátorů byl v roce 1979 prodej ukončen. Nového obrození se mu dostává v současné době, jako ukázka až zázračné optimalizace kódu do malého prostoru. Zde naleznete online emulátor s podrobným popisem kalkulátoru: http://files.righto.com/calculator/sinclair_scientific_simulator.html a dočkal se i stavby mnoha replik: https://simpleavr.github.io/tms0800/index.html , https://hackaday.com/2018/06/22/your-own-sinclair-scientific-calculator/ .
Na podobných principech jako Datamath pracuje i programovatelný kalkulátor TI-57 od Texas Instruments, s procesorem TMC1501, rok 1977. Číslo je uloženo ve 14 BCD číslicích. Nejvyšší číslice, D13, obsahuje flagy: B0 negativní mantisa, B1 negativní exponent, B2 inverze. Inverze znamená, že mantisa byla dočasně změněna na dvojkový doplněk. Číslice D12 až D2 obsahují 11 číslic mantisy (v D12 je nejvyšší číslice). Číslice D1 a D0 obsahují exponent 00 až 99 (v D1 je vyšší číslice). Příklad zobrazení čísla -75.43:
Online emulátor kalkulátoru: https://www.pcjs.org/machines/ti/ti57/rev0/ .
Populární kalkulačka TI-59 (rok 1978) od Texas Instruments používá procesor TMC0501E, má více paměti než TI-57 a i k uložení čísla používá větší registry. Číslo je uloženo v 16 BCD číslicích. Číslice D15 až D3 obsahují 13 číslic mantisy (v D15 je nejvyšší číslice). Číslice D2 a D1 obsahují exponent 00 až 99 (v D2 je vyšší číslice). Buňka D0 obsahuje flagy: B0 negativní mantisa, B1 negativní exponent, B2 inverze. Příklad zobrazení čísla -75.43:
Od kalkulaček se dostáváme k 8-bitovým počítačům. Od BCD kódu se přechází k binární reprezentaci čísla, která je pro 8-bitové procesory výhodnější kvůli rychlejším výpočtům.
Populární počítače ZX-81 (rok 1981) a ZX-Spectrum (rok 1982) ukládají čísla do 5 bajtů v binárním kódu. První bajt, D0, nacházející se na nejnižší adrese, obsahuje exponent s biasem (=ofsetem) 0x80 (128). Na rozdíl od kalkulaček, pracujících v BCD kódu, kde je exponent přímo mocninou základu 10, zde se jedná o binární exponent. Představuje počet bitových posunů (neboli násobení a dělení číslem 2), o které musíme mantisu posunout, aby se dostala do intervalu 0.5 (včetně) až 1.0 (vyjma). K počtu posunů přičteme bias 0x80, neboli střední hodnotu exponentu. To je z toho důvodu, abychom s exponentem mohli pracovat jako s kladným číslem - přece jen se s číslem bez znaménka v procesorech pracuje snáz než se znaménkovým číslem (při operacích posunů se generuje flag carry, který lze využít ke kontrole přetečení).
Pozor na odlišnost interpretace biasu od normy IEEE754. U ZX Spectra se považuje mantisa za normalizovanou v intervalu 0.5 <= až < 1.0 (bias se uvádí 0x80), zatímco u float čísla normy IEEE754 je normalizovaná mantisa v intervalu 1.0 <= až < 2.0 (bias 0x7F). Číslo "1.0" v ZX Spectru je uloženo jako 0x81 0x00 0x00 0x00 0x00 (exponent má hodnotu 0x81) a v normě IEEE754 jako 0x00 0x00 0x80 0x3F (exponent má hodnotu 0x7F). Exponent se tak liší o 2, přestože uváděné biasy se liší o 1.
Následující 4 bajty obsahují mantisu. V prvním bajtu (na nejnižší adrese) je nejvyšší bajt mantisy. Mantisa se používá v zarovnaném tvaru - tj. číslo se posouvá do vyšších řádů tak dlouho, až se na nejvyšším bitu mantisy objeví nejvyšší bit s hodnotou "1". Vzhledem k tomu, že číslo je vždy zarovnané, nacházel by se na nejvyšší bitové pozici mantisy vždy bit s hodnotou "1". Můžeme tedy tento bit vypustit a na jeho místo dosadit znaménkový bit.
Výjimečnou hodnotou je číslo 0. To bychom nemohli vyjádřit exponentem a mantisou (u mantisy jsme zrušili nejvyšší bit "1", který bychom jinak využili k indikaci nuly). Speciální případ čísla 0 se vyjádří exponentem s hodnotou nula. Exponent tak bude nabývat hodnoty 0x01 až 0xFF pro platná nenulová čísla a hodnotu 0x00 pro číslo 0.
Binární exponent má rozsah +-127 (0x01-0x80 až 0xFF-0x80). Pro zjištění rozsahu výsledného dekadického exponentu vynásobíme binární exponent hodnotou log10(2). Tedy 127 * 0.30103 = 38.2. Dekadický exponent má rozsah +- 38.
Přesnost mantisy spočítáme tak, že 1 bit představuje hodnotu log10(2) = 0.30103. Mantisa má 32 bitů (včetně skryté "1"), její přesnost bude 32 * 0.30103 = 9.6 číslic. Počítač provádí interní výpočty s přesnosti na 9.6 číslic, ale protože se nějaká přesnost ztratí během interních mezivýpočtů, zobrazuje pro uživatele výsledek zaokrouhlený na 8 číslic.
ZX Spectrum jde ve formátu ještě dál (neplatí pro ZX-81). Exponent s hodnotou 0 používá ještě k dalšímu účelu - celé číslo. Má-li první bajt D0 hodnotu 0, použijí se následující 2 bajty D1 a D2 jako celé číslo se znaménkem. Číslo s hodnotou 0 musí být tedy vyjádřeno nulovým obsahem 3 prvních bajtů. Použití celého čísla jako součást formátu má velkou přednost v tom, že se jednak neztrácí přesnost během float operací a jednak výpočty s celými čísly mohou být rychlejší. Během výpočtů Spectrum nejdříve zkontroluje, zda jsou oba operandy celočíselné a zda se výsledek vejde opět do celého čísla. Pokud ano, provede se zrychlený výpočet pomocí celých čísel. Pokud ne, převedou se oba operandy na float čísla a výpočet se provede klasicky pomocí float aritmetiky.
Kombinace float a integer formátu má velký význam především u jazyků používajících jediný číselný formát, jako je typicky BASIC. Použije-li se float číslo např. jako čítač smyčky, může po více iteracích dojít k nezanedbatelné odchylce od správné hodnoty a počet průchodů smyčkou může "ujet". Což je pochopitelné z pohledu binární reprezentace float čísla, ale špatně pochopitelné pro uživatele. Spectrum v takovém případě použije pro čítač celá čísla a k chybné funkci nedojde.
V příkladu vidíte, jak v ZX Spectru vypadá číslo s hodnotou -75.43. V hexadecimálním tvaru jsou to hodnoty 0x87 0x96 0xDC 0x28 0xF6. Jak jsme k takovému číslu dospěli? Vezmeme číslo bez znaménka, 75.43. Číslo opakovaně dělíme 2 až do chvíle, kdy se dostane do intervalu 0.5 (včetně) až 1.0 (vyjma). Potřebujeme k tomu 7 posunů, tedy vydělení číslem 128. Po přičtení biasu 0x80 bude první bajt s exponentem mít hodnotu 0x80 + 7 = 0x87.
Vzniklou mantisu 0.589296875 dále převedeme na celočíselné vyjádření. Výsledkem má být 4-bajtové číslo, vynásobíme proto mantisu číslem 2^32. Tedy 0.589296875 * 2^32 = 2531010805.76. Po zaokrouhlení na celé číslo 2531010806, v hex kódu 0x96DC28F6. Odstraníme nadbytečný nejvyšší bit "1", číslo bude 0x16DC28F6. A protože číslo je záporné, nastavíme znovu nejvyšší bit a obdržíme výslednou hodnotu dalších 4 bajtů, 0x96 0xDC 0x28 0xF6 (ukládáme do paměti od nejvyšších bajtů).
Další příklady čísel: 1.0 = 0x81 0x00 0x00 0x00 0x00, pi = 0x82 0x49 0x0F 0xDA 0xA2, 10 = 0x00 0x00 0x0A 0x00 0x00 (pro ZX-81 10 = 0x84 0x40 0x00 0x00 0x00).
Povšimněte si pořadí bajtů. Číslo se v ZX Spectru ukládá od nejvýznamnějších bajtů, počínaje exponentem. Důvodem je to, že test exponentu na nulu je nejčastější operací s číslem, proto je výhodnější aby se nacházel na začátku čísla. Druhou nejčastější operací je test znaménka, které se nachází v druhém bajtu čísla.
AMOS z roku 1986 je operační systém pro počítače IQ 151. Reprezentace čísel typu "real" v AMOS Pascalu je téměř shodná s novodobým tvarem čísel v přesnosti single-precision (float). Příklad čísla -75.43:
Číslo je uloženo ve 4 bajtech. Nejvyšší bajt, D3, obsahuje exponent. Exponent je 8-bitové číslo bez znaménka, s biasem (ofsetem) 0x7F (127). Exponent představuje počet binárních posuvů (násobení a dělení 2x), abychom mantisu dostali do rozsahu 1.0 (včetně) až 2.0 (vyjma). Mantisa je uložena do bajtů D0 až D2, nejvyšší bit "1" je skrytý a nahrazen znaménkovým bitem. Dekadický exponent má rozsah +- 38, mantisa má přesnost 7.2 číslic, zobrazuje se na max. 6 číslic. Další příklady čísla: 1.0 = 0x00 0x00 0x00 0x7F, -1.0 = 0x00 0x00 0x80 0x7F, pi (3.1415927) = 0xDB 0x0F 0x49 0x80.
Znaménkový bit není uložen v nejvyšším bitu čísla (nad exponentem), jak by to odpovídalo IEEE754 normě, ale v nejvyšším bitu mantisy, namísto skrytého bitu "1". To je výhodnější pro softwarovou implementaci - s exponentem lze snadno manipulovat jako s jedním bajtem a znaménkový bit může být během výpočtů nahrazen zpět skrytým bitem.
Novodobé počítače dodržují jako standard čísel normu IEEE754, z roku 1985. Jako typického zástupce si ukážeme formát čísla double-precision.
(zdroj obrázku: Wikipedie)
Double číslo má velikost 64 bitů (8 bajtů). Nejnižších 52 bitů obsahuje mantisu. Mantisa je uložena bez nejvýznamnějšího bitu "1". Ve skutečnosti by tedy měla 53 bitů. Znaménkový bit není uložen spolu s mantisou, namísto skrytého bitu "1", ale je uložen v nejvyšším bitu 63 čísla. To má opodstatnění ve snadném porovnávání a třídění čísel - čísla lze porovnat jednoduše jako bajty. Současně je to bohužel tak trochu nepříjemnost pro softwarovou implementaci výpočtů - během výpočtů je užitečné dočasně obnovit skrytý bit "1" náhradou za znaménkový bit, což u tohoto formátu nelze udělat. Ale až tak velký problém to není, protože interní výpočty je stejně vhodnější dělat s vyšší přesností (aby se neakumulovala chyba mezivýpočtů) a formáty převádět. To dělá i matematický koprocesor, který během výpočtů používá jako interní formát "extended precision" 80 bitů s obnoveným skrytým bitem. Dosažitelná přesnost mantisy 53 * 0.30103 = 16.0 číslic.
Exponent je 11 bitů, s biasem (ofsetem) 0x3FF (1023). Exponent představuje počet bitových posunů (neboli násobení a dělení číslem 2), o které musíme mantisu posunout, aby se dostala do intervalu 1.0 (včetně) až 2.0 (vyjma). K počtu posunů přičteme bias 0x3FF, neboli střední hodnotu exponentu. Pro výpočet rozsahu exponentu vynásobíme binární exponent číslem log10(2), tedy 1023 * 0.30103 = 308. Dekadický exponent má rozsah +- 308.
Double-precision číslo používá některé hodnoty exponentu jako speciální případy. Běžná čísla jsou reprezentována rozsahem 0x001 až 0x7FE. Speciální hodnota exponentu 0x000 slouží k indikaci jednak nuly (nula může mít kupodivu znaménko) - to v případě, že mantisa má nulový obsah - a jednak čísel s malým exponentem, "subnormals". Subnormals jsou malá nenormalizovaná čísla, u kterých je nastaven nejvyšší bit "1". Nejvyšší bit se v tom případě nemusí nacházet na nejvyšší pozici mantisy.
Opačný konec, exponent s hodnotou 0x7FF, je vyhrazen k indikaci čísla mimo rozsah: 1#INF, -1#INF, 1#NAN, -1#NAN.
Příklady čísel (bajty v pořadí od nejnižší adresy, opačně tedy než bity na obrázku): 1.0 = 0x00 0x00 0x00 0x00 0x00 0x00 0xF0 0x3F, pi (3.1415926535897932) = 0x18 0x2D 0x44 0x54 0xFB 0x21 0x09 0x40.
Čísla s jednoduchou přesností, neboli "float", mají velikost 32 bitů (4 bajty). Nejnižších 23 bitů obsahuje mantisu, bez skrytého nejvyššího bitu "1". Dosažitelná přesnost mantisy je 24 * 0.30103 = 7.2 číslic. Znaménko je uloženo v nejvyšším bitu čísla 31. Mezi mantisou a znaménkem se nachází 8 bitů exponentu, s biasem 0x7F (127). Dekadický exponent má rozsah 127 * 0.30103 = +- 38.
(zdroj obrázku: Wikipedie)
Příklady čísel (od nejnižších bajtů): 1.0 = 0x00 0x00 0x80 0x3F, pi (3.1415927) = 0xDB, 0x0F, 0x49, 0x40.
Nejsme-li vázáni na dodržení norem, můžeme si vymyslet jakýkoliv formát float čísel. Kalkulátor ET-58 je replika vycházející z populární TI-58 a rozšiřující vlastnosti původní kalkulačky. Čísla jsou zde uložena do 10 bajtů, v binárním formátu. V prvních 2 bajtech je exponent s biasem 0x8000 (32768). Rozsah dekadického exponentu je +-9863. V následujících 8 bajtech je mantisa, uloženo od vyšších bajtů k nižším, se skrytým bitem "1" nahrazeným znaménkem. Přesnost mantisy je 19.3 číslic. Příklad čísla -75.43:
Postup při převodu čísla do interního formátu: Číslo se postupným dělením 2 dostane do intervalu 1.0 (včetně) až 2.0 (vyjma). Pro číslo 75.43 to je 6 posuvů. Exponent s biasem 0x8000 bude mít hodnotu 0x8006, to jsou první 2 bajty čísla. Po vydělení exponentem bude číslo 1.17859375. Abychom mantisu dostali do potřebného tvaru vyjádřeného 8 bajty celého čísla, musíme ji vynásobit číslem 2^63 (mantisa bude mít 64 bitů, z toho 1 bit už je zahrnut v intervalu 1..2). Dostaneme 1.17859375 * 2^63 = 10870608636561808424.96. Po zaokrouhlení na celé číslo 10870608636561808425. V HEX tvaru to je 0x96DC28F5C28F5C29. Odstraníme skrytou nejvyšší "1": 0x16DC28F5C28F5C29. A opět doplníme nejvyšší "1", protože číslo je záporné: 0x96DC28F5C28F5C29. Dalších 8 bajtů čísla tedy bude: 0x96 0xDC 0x28 0xF5 0xC2 0x8F 0x5C 0x29.
www stránka projektu: ../../hw/et58/
Miroslav Němeček