Reprezentácia necelých čísel

© 2 November 2007 by Marek Behún, pôvodne na blackhole.sk

Ako to je s reprezentáciou necelých čísel v počítačoch, ako sa ukladajú do pamäte.

Na začiatok by sa chcelo vidieť niečo o tom, ako to vlastne s číslami v počítaci vôbec je.
Začneme celými číslami.
Majme C program:

int main () {
  int a = 5;
  printf("%i\n", a);
}

Tento program si vyhradí nejaké miesto v pamäti do ktorého je schopný uložit číslo typu int (int a = 5). Typ int zaberá v pamäti 4 bajty = 32 bitov (1 bajt = 8 bitov). To znamená, že číslo 5, ktoré doň uložíme, zaberie 32 bitov, a v dvojkovej sústave sa zapíše ako:
5 = 00000000 00000000 00000000 00000101
(Obyčajný prevod do dvojkovej sústavy).

Takto možno dosiahnúť celkovo 232 = 4,294,967,296 rôznych čísel. Typ int môže obsahovať aj zápornú hodnotu, a to počítaním z opačnej strany - odpočítavaním z najvyššej hodnoty 232, napríklad číslo -1 bude reprezentované ako 232-1:

+3 = 00000000 00000000 00000000 00000011
+2 = 00000000 00000000 00000000 00000010
+1 = 00000000 00000000 00000000 00000001
+0 = 00000000 00000000 00000000 00000000
-1 = 11111111 11111111 11111111 11111111
-2 = 11111111 11111111 11111111 11111110

To znamená, že int môže obsahovať hodnoty od -231 do 231-1 (od -2147483648 do 2147483647), čo je dohromady spomínaných 232 cisel (aj s nulou).

Tiež celočíselné typy short a char sú na tom takto:

short - (2 bajty = 16 bitov)
  2^16 = 65,536 rôznych hodnôt od -32768 do 32767
char - (1 bajt = 8 bitov)
  2^8 = 256 rôznych hodnôt od -128 do 127.

Poprípade ešte je 8 bajtov = 64 bitov dlhá hodnota používaná pre veľké čísla (v Céčku sa nazýva long long int).

Ak dopredu vieme, že nebudeme používať záporné čísla, premennej môžme prideliť predponu unsigned (unsigned int, unsigned short, ...).
Potom ma napríklad typ unsigned int možné hodnoty od 0 do 232-1 (od 0 do 4294967295).

Súhrn:

8B = long long int ( = long long )
4B = long int ( = long = int )
2B = short int ( = short )
1B = char

char         od -128 do 127
short        od -32768 do 32767
int          od -2147483648 do 2147483647
long long    od -9223372036854775808 do 9223372036854775807

unsigned char         od 0 do 255
unsigned short        od 0 do 65535
unsigned int          od 0 do 4294967295
unsigned long long    od 0 do 18446744073709551615



Desatinné čísla

V prípade desatinných čísel je to inak. Kedže nevieme, kde v čísle sa bude nachádzať desatinná čiarka (a zapísať ju niekde pevne, napríklad že má byť presne v polovici čísla, nie je výhodné, pretože na väčšie čísla by sme potrebovali veľa pamäte), vyriešilo sa to inak.
Najviac je používaný Medzinárodný štandard pre binárnu desatinnú aritmetiku IEEE 754.

V tomto článku opíšem IEEE 754 single precision. V Céčku sa takémuto typu nadáva float.

Každé číslo sa skladá z troch častí: znamienko, exponent a mantissa.
Pre uloženie do 32 bitového priestoru (single precision) je zloženie desatinného čísla takéto:

S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
S - sign (znamienko) 1 bit
E - exponent 8 bitov
M - mantissa 23 bitov

hodnota = (-1)S × 2E-127 × (1.M)

To znamená, že prvá časť určuje znamienko:
ak S = 0, číslo je kladné,
ak S = 1, číslo je záporné.

Druhá časť je umocnenie čísla 2 na exponent (od ktorého sa odpočíta hodnota 127).

Tretia časť mantissa je vlastne zvyšný podiel, ktorým treba násobiť, aby sme dosiahli naše číslo. POZOR: Mantissa obsahuje skrytú jednotku na začiatku, ktorá tam je vždy a preto sa nezapisuje (1.M). Napríklad ak je mantissa zapísaná ako:

00110011001100110011001
v skutočnosti to je
1.00110011001100110011001

Prečo sa od exponentu odčítavá hodnota 127? Je to preto, aby sme mohli dosiahnúť presnosť za desatinnou čiarkou (teda aj záporné hodnoty, ak E=3, v skutočnosti je exponent 3-127 = -124).

príklad premeny 0.75:
Chceme premeniť číslo 0.7510 do štandardu IEEE 754 signle precision.
0.7510 = 0.112      (premenené do dvojkovej sústavy)
0.7510 = 1.1 × 2-1  (normalizované dvojkové číslo)

číslo sme normalizovali preto, aby sme na začiatku mali pred desatinnou čiarkou jednotku, ktorá je skrytá.

číslo je kladné: S = 0.
Exponent pri dvojke je -1, E je vždy zväčšené o 127:
E = -1 + 127 = 126 = 01111110.
Desatinná časť mantissy:
M = 10000000000000000000000 (23 bitov)

číslo 0.75 sa vo formáte IEEE 754 single precision reprezentuje ako:
0 01111110 10000000000000000000000  (32 bitov)


príklad premeny -2345.125:
-2345.12510 = -100100101001.0012
-2345.12510 = -1.00100101001001 × 211  (normalizované)
číslo je záporné: S = 1.
Exponent pri dvojke je 11, E je zväčšené o 127:
E = 11 + 127 = 138 = 10001010.
M = 00100101001001000000000 (23 bitov)

číslo -2345.125 sa vo formáte IEEE 754 single precision reprezentuje ako:
1 10001010 00100101001001000000000  (32 bitov)


Špeciálne prípady
Exponent E môže byť v rozmedzí od 0 do 255. Odčítaný môže byť v rozmedzí -127 do 128. V skutočnosti však iba od -126 do 127, pretože hodnoty -127 a 128 (E=00000000 a E=11111111) sa používajú na špeciálne prípady čísel:
Ak E = 0 a S = 0
  hodnota čísla = 0
Ak E = 0 a S = 1
  nenormalizované číslo, (výsledok tzv. underflowu pri násobení)
Ak E = 255 a S = 0
  infinity (nekonečno). Napríklad pri delení nulou.
Ak E = 255 a S = 1
  NaN (Not A Number). Napríklad pri delení nuly nulou.