přihlásit 9944/535237

Číselné soustavy a převody mezi nimi

Co to jsou číselné soustavy

Číslo představuje hodnotu něčeho, třeba množství, rychlost nebo váhu. Lidstvo si během věků vytvořilo řadu různých číselných soustav, v kterých je stejná hodnota uvedena jiným číslem. Dnes se běžně a převážně používá desítková (decimální) soustava, v které se všechny čísla skládají z deseti cifer 0 až 9.

Programátoři ale používají i další soustavy a to dvojkovou (binární), osmičkovou (oktalovou) a šestnáctkovou (hexadecimální). Obecně lze také hovořit o číselné soustavě o základu 2, 8, 10 a 16. Čistě teoreticky lze mít i soustavy o jiném základu, ale jestli se někde používají, tak velmi vzácně. Základ soustavy nám říkám, kolik používá samostatných cifer.

Další kapitolou je zápis čísla. My jsme zvyklí používat arabské číslice, (ale známe třeba i římská čísla), které používáme k zápisu všech soustav. U soustav se základem větším než 10 si pak pomáháme písmeny abecedy. U šestnáctkové soustavy tak znak A nese hodnotu deset, B jedenáct a končí to znakem F, který nese hodnotu patnáct. Oproti tomu u dvojkové soustavy se veškerá čísla zapisují jen pomocí dvou znaků, jedna a nula. Pro lepší představu uvádím tabulku 32 prvních hodnot v různých soustavách

Soustava o základu
102816102816
0 0 0 016 100002010
1 1 1 117 100012111
2 10 2 218 100102212
3 11 3 319 100112313
4 100 4 420 101002414
5 101 5 521 101012515
6 110 6 622 101102616
7 111 7 723 101112717
8 100010 824 110003018
9 100111 925 110013119
10 101012 A26 11010321A
11 101113 B27 11011331B
12 110014 C28 11100341C
13 110115 D29 11101351D
14 111016 E30 11110361E
15 111117 F31 11111371F
321000004020

Jak je možno si všimnout, tak číslo zapsané jako 10 v různých soustavách znamená různou hodnotu. Dva v binární, osm v oktalové, deset v desítkové a šestnáct v hexadecimální. Proto když někde vidíme zapsané číslo, musíme vědět v jaké je soustavě, abychom rozuměli jeho hodnotě.

Zápis v Pythonu

Python umí přímo pracovat se všemi výše uvedenými soustavami. Aby Python mohl rozlišit v které soustavě je číslo zapsáno, zapisuje se před samotnou hodnotu rozlišovací prefix. Tyto prefixy jsou standardní a používají se i v jiných programovacích jazycích. Číslo zapsané do zdrojového kódu s prefixem je literál čísla. Více se o literálech dočtete v kapitole o datových typech.

soustava základ prefix příklady poznámka
binární2 0b0b1, 0b0001, 0b1111, 0b... od Pythonu 3.0
oktalová8 001, 010, 01234, 0... do Pythonu 2.x včetně
oktalová8 0o0o1, 0o10, 0o1234, 0o... od Pythonu 3.0
decimální10 0, 1, 10, 1234, ...
hexadecimální16 0x0x1, 0x10, 0xFF, 0x89AB, 0x...

Nyní už by mělo být jasné, že tato čísla se rovnají, protože mají stejnou hodnotu:

0b1010 == 0o12 == 10 == 0xA

A naopak, že tato čísla se nerovnají, protože mají různou hodnotu:

 0b10 != 0o10 != 10 != 0x10

Zvláště varuji před "okrasným" přidáváním nul na začátek čísla. To je chyba, která by laika mohla pěkně potrápit a možná by tento mohl Python zavrhnout, jako naprosto neschopný jazyk, který neumí sečíst ani dvě jednoduchá čísla:

>>> 010 + 010
16

Pamatujte si, že přidáním nuly na začátek čísla změníte u Pythonu do verze 2.x jeho hodnotu. U Pythonu 3.0 to není povoleno a dojde k vyvolání výjimky "SyntaxError: invalid token".

Převod čísel mezi soustavami

literál na text

Interně má Python hodnotu celého čísla uloženu jako datový objekt typu int (u Pythonu 2.x byla ještě možnost long). Na tento datový objekt se automaticky převádí libovolný literál celého čísla. Při převodu datového objektu typu int na řetězec (třeba pro výpis na obrazovku) se tento převádí na číslo v decimální podobě (ať byl literál jakékoli soustavy, protože datový objekt typu int si nepamatuje informaci o číselné soustavě literálu, na základě kterého byl vytvořen). To je možno si jednoduše vyzkoušet:

>>> str(0b10), str(0o10), str(10), str(0x10)
('2', '8', '10', '16')

Python načte literál čísla, převede ho na datový typ int a předá ho funkci str(). Ta u každého datového objektu volá metodu __str__(), která slouží pro převod datového objektu do textové podoby. Tato je pak vypsána na terminál.

Pokud bychom potřebovali textovou podobu v jiné číselné soustavě, máme na to v Pythonu k dispozici speciální funkce bin(), oct() a hex(). Funkce bin() je k dispozici pouze u Pythonu 3 a vyšším. Ve starších Pythonech nejsou binární literály k dispozici. Příklad použití:

>>> bin(10), oct(10), hex(10), bin(0xA), oct(0xA)
('0b1010', '0o12', '0xa', '0b1010', '0o12')

Jak je vidět, tyto funkce jako parametr přijímají literál libovolné číselné soustavy a tak lze tyto soustavy převádět navzájem. Povšimněte si rovněž, že funkce hex() používá v literálu malá písmena abecedy. Je to jedno, u hexadecimálních literálů Python velikost znaků nerozlišuje.

text na literál

Je nutno si uvědomit, že literály čísel nejsou textové řetězce a Python s nimi pracuje jako s čísly. Tedy je umí např. sečíst:

>>> 10 + 0xAA
180

Když Pythonu předložíme řetězce, tak je nesečte, ale spojí:

>>> '10' + '0xAA'
'100xAA'

Proto je důležité rozlišovat mezi literály čísel a textovými řetězci obsahující literály čísel. Pokud máte k dispozici literál celého čísla v řetězci (tedy datovém objektu typu str), musíte si ho před použitím převést na číslo (tedy datový objekt typu int). K tomu slouží funkce int(). Tato funkce umí převádět na int čísla všech soustav o základu 2 až 36. Základ se musí uvádět jako parametr s výjimkou základu 10, který je přednastavená. V řetězci může být číslo s prefixem literálu i bez něj. Ale i v případě že je s prefixem, je nutno základ uvést, protože funkce int() se základ čísla podle prefixu nesnaží zjistit.

int('10')       =>  10 # typické použití převodu řetězce na číslo
int('10',   10) =>  10 # to samé s uvedeným základem 10
int('10',    2) =>   2 # se základem 2 text '10' znamená jiné číslo
int('0b10',  2) =>   2 # to samé, správný prefix není na závadu
int('0b10',  8) => err # toto nelze, nesprávný prefix pro základ 8
int('0o10',  8) =>   8 # toto už lze, prefix a základ je v souladu
int('10',    8) =>   8 # taky žádný problém, prefix není nutný
int('20',   16) =>  32 # hexadecimální soustava také není problém
int('0x20', 16) =>  32 # ani s prefixem
int('x',    36) =>  33 # v soustavě o základu 36 znak 'x' má hodnotu 33

Teď už si tedy umíme poradit i s touto úlohou. Máme textový řetězec s číslem 0x123456789ABCDEF. Převeďte jej na řetězec, kde je toto číslo v binární podobě.

>>> bin(int('0x123456789ABCDEF', 16))
'0b100100011010001010110011110001001101010111100110111101111

A kdyby nám vadil ten binární prefix, tak potom takto:

>>> bin(int('0x123456789ABCDEF', 16))[2:]
'100100011010001010110011110001001101010111100110111101111'

Pro úplnost. V Pythonu jsou i jiné funkce pro převod textového řetězce na číslo. Např. funkce float() slouží pro převod desetinného čísla v řetězci. Např: float('10.0') => 10.0
Desetinná čísla nejsou jiná číselná soustava, ale jiný datový typ. Python umožňuje pracovat s desetinnými čísly jen v desítkové soustavě. To znamená, že např. neexistuje literál pro zápis čísla 0.5 v oktalové soustavě, kterému by Python rozuměl. Proto i zápis oct(0.5) skončí s chybou.