Číselné datové typy

Unsigned

Čísla v procesoru mají vždy omezený rozsah. Když výsledek nějaké početní operace tenhle rozsah přeteče, nadbytečné číslice se prostě ztratí a je na nás, abychom na to dali pozor. C++ (a další jazyky) nabízejí jednobajtový typ unsigned char, do kterého jde uložit číslo v rozsahu 0 až 255.

Dále máme spoustu typů na způsob unsigned short, unsigned int, unsigned long, jejichž velikost závisí na architektuře procesoru a není úplně dobré na ni spoléhat. Když chceme mít počet bajtů přesně pod kontrolou, použijeme například std::uint32_t z hlavičkového souboru #include <cstdint>

Signed

Záporná čísla zavedeme jako two's complement, protože se správně chová ke sčítání a odečítání. Do typu char se vejdou čísla od -128 do +127. Čísla 0 až 127 převádíme beze změny, následující číslo je pak -128 a dále hodnoty stoupají až k -1. Hodnoty v tomhle zápisu můžete sčítat obyčejně, jako by to byla přirozená čísla.

Endianita

Důležitý pojem je pořadí bajtů. Když má například int osm bajtů, může nás trápit, jestli jsou (jako v našem lidském zápisu) číslice nejvyššího řádu na začátku, nebo naopak první bajt začíná číslicemi s vahou 1 až 128. Na obvyklých počítačích dnes jednoznačně převažuje little endian, kdy první bajt má čísla s nejmenší vahou. Tenhle způsob zápisu má velkou výhodu, že dostatečně malé číslo má ve formátech char, short int, int apod. stejný začátek. Při troše odvahy nemusíme opravdový formát takového čísla ani znát.

Floating-point

Kromě toho někdy chceme pracovat s čísly racionálními. Vezmeme si za vzor vědecký zápis čísel, kdy třeba hmotnost elektronu zapisujeme 9.109·10-31 kg. Hodnota 9109 je mantisa neboli signifikand, hodnota -31 je exponent. Exponent nám vlastně určuje, kam máme v mantise dát desetinnou čárku: kdyby byl nulový, dáme ji za první číslici, jinak ji posuneme o příslušný počet míst směrem doprava (v případě kladného exponentu). Tohle se nazývá zápis s plovoucí řádovou čárkou, anglicky floating point.

Ve dvojkové soustavě použijeme tedy obdobně mocniny dvojky a všimneme si, že první číslice mantisy je vždycky jednička – kdyby ne, posuneme si exponent tak, aby tam jednička byla a o nic nepřijdeme. Protože jsme šetřiví, nebudeme tenhle jeden bit ani ukládat do paměti a jen si ho představíme. Doopravdy to funguje, akorát se pak nedá uložit nula, která opravdu nemá mít jedničku nikde.

Pro uložení nuly si zavedeme speciální hodnoty, a to dokonce tak, že trochu jinou hodnotou označíme +0 a -0. Procesor beztak s tímhle číselným typem počítá pomalu, takže to nevadí (musí dávat pozor, aby platilo +0 = -0). A když už jsme začali, můžeme si rovnou zavést kladné a záponé nekonečno (vychází například z dělení nulou) a speciální hodnotu Not a Number, když třeba odečteme dvě nekonečna od sebe. Může se to zdát jako prasárna, ale takováhle aritmetika občas hodně usnadní práci.

Obyčejný typ float má mantisu tříbajtovou a jednobajtový exponent, častěji se ale používá typ double, který má mantisu o 53 bitech (tedy necelých sedm bajtů) a exponent o 11 bitech.

Používání čísel ve formátu floating point bývá dobré se vyhnout, pokud je opravdu nepotřebujeme. Do výpočtů obecně vnášejí nevyzpytatelné zaokrouhlovací chyby; především, nedají se pomocí nich vyjádřit zlomky s něčím jiným než mocninou dvojky ve jmenovateli. Stejně, jako v desítkové soustavě má 1/3 nekonečný desetinný rozvoj, dělá ve dvojkové soustavě takové problémy i 1/5. Když v téměř jakémkoli jazyce sečtete 0.1+0.1+... desetkrát, vyjde vám něco jako 0.999. Třeba v peněžních operacích by to mohlo napáchat velké škody.

Místo toho si můžeme zavést čísla s pevnou desetinnou čárkou (třeba počítat všechno v tisícinách), nebo použít 10 jako základ exponentu, nebo dokonce počítat se zlomky ve formátu čitatel, jmenovatel. Každý způsob má svoje zřejmá omezení - ten poslední naráží snad akorát na problém, že je strašně pomalý. Floating point je naopak bezpečné používat například v grafice a vědeckých výpočtech, kde nás přesnost tolik netrápí. Vůbec naopak není rozumné je používat v cyklech, protože by program kvůli zaokrouhlování nemusel skončit nikdy.

Převod mezi číselnými soustavami

Čísla jsou obyčejně v desítkové soustavě. Když se v počítači vyskytuje číslo v šestnáctkové (hexadecimální) soustavě, má předponu 0x a pro číslice 10 až 15 používáme znaky A až F. Například 255 je 0xFF. Čísla v binární soustavě mají obdobně předponu 0b, tedy 0xFF je 0b11111111. Osmičková soustava se používá zřídka, o to je překvapivější její předpona: samotná 0, proto 0377 znamená ve skutečnosti v desítkové soustavě (opět) 255.

Když nás zajímá zápis nějakého čísla v soustavě o určitém základu, nejlepší je, když to číslo umíme rychle dělit se zbytkem. Pak stačí dělit zadané číslo základem b, zbytek zapisovat jako číslice výsledku zprava doleva (!) a dělení vždy opakovat pro výsledný podíl. Až se dostaneme na podíl 0, přestaneme.

Obdobný postup se dá použít, když máme číslo zapsané v nějaké soustavě a chceme jej nějak zpracovat. Zavedeme si nějakou proměnnou a na začátku ji nastavíme na nulu. Číslice čteme tentokrát zleva, každou do proměnné přičteme a tento součet vynásobíme základem b. To je mimochodem postup známý jako Hornerovo schéma - obecně velmi účinný algoritmus, který je dobré mít na paměti.

Všimněte si, že v obou případech nás nezajímá soustava jednoho z čísel - je důležité jen, abychom jej uměli rychle dělit nebo násobit. Z toho ohledu vlastně ani nemusíme vědět, že počítače pracují ve dvojkové soustavě... ale opravdu to tak je ;) Význam to může mít, pokud převádíme mezi soustavami, které mají soudělné základy. Obzvlášť, když je jeden základ mocninou druhého, například při převodu z dvojkové do šestnáctové soustavy. Potom nepotřebujeme žádné početní operace - každou čtveřici bitů na vstupu přeložíme na jedno šestnáctkové číslo (když počet číslic nevychází, dopíšeme zleva nuly). Na ukázku - 0b11 jsou 3 = 0x3, 0b1011 je 11 = 0xB. Proto 0b111011 je 0x3B.

Když převádíme mezi sousavami v hlavě (třeba z desítkové do binární), je možná pohodlnější odečítat postupně mocniny dvojky, protože dělení nám lidem prostě trvá dlouho. Ať vás ale nenapadne tímhle způsobem převádět čísla v počítači - pro větší čísla to je zbytečně pomalé.