Systemy liczbowe

Dlaczego komputery liczą w dwójkowym, a programiści myślą w szesnastkowym

14 lip 2026·10 min czytania·1870 słów
Świecące szmaragdowe cyfry 0 i 1 splatające się w geometryczną siatkę z glifami szesnastkowymi A–F, na tle kosmicznej mgławicy w fiolecie i magencie

Liczba 255, zapis 0xFF, 0o377 i 0b11111111 to cztery różne ubrania tej samej wartości. Nic w tej liczbie nie „jest" dziesiętne — dziesiątka to nasz nawyk, odziedziczony po anatomii dłoni, a nie własność samej liczby. Komputer tego nawyku nie ma: jego świat kończy się na dwóch stanach napięcia. Cała różnorodność zapisów, którą programista widzi na ekranie, bierze się właśnie z tego rozjazdu — maszyna liczy inaczej, niż myśli człowiek, i ktoś musi pójść na kompromis.

Wartość to nie zapis

W systemie pozycyjnym każda cyfra ma wagę zależną od miejsca, w którym stoi. Liczbę o podstawie r rozkłada się na sumę potęg:

N = aₙ·rⁿ + aₙ₋₁·rⁿ⁻¹ + … + a₁·r¹ + a₀·r⁰, gdzie cyfry aᵢ pochodzą z zakresu od 0 do r−1.

Podstawa jest umowna. Umowne są też glify: to, że wartości od dziesięciu do piętnastu zapisujemy literami A–F, wynika wyłącznie z tego, że alfabet miał je pod ręką. Ścisła jest za to sama arytmetyka — konwersja między podstawami niczego nie zaokrągla i nie gubi, bo nie zmienia wartości, tylko jej ubranie.

Stąd bierze się najczęstsze potknięcie początkujących: zapis 10 sam z siebie nie znaczy nic. W systemie dwójkowym to dwa, w ósemkowym osiem, w szesnastkowym szesnaście. Bez deklaracji podstawy ciąg cyfr jest po prostu wzorem graficznym.

Żeby taki system w ogóle działał, potrzebne jest zero — nie jako „nic", lecz jako znacznik pustego rzędu, odróżniający 205 od 25. Babilończycy, którzy zbudowali imponujący system sześćdziesiętny (opisujemy go w tekście o tym, dlaczego godzina ma 60 minut), konsekwentnego zera nie mieli i wartość trzeba było odczytywać z kontekstu. Domknęli to dopiero matematycy indyjscy: kropka bindu z manuskryptu z Bakhshali (datowanie radiowęglowe z Oksfordu wskazuje na III–IV wiek n.e., choć bywa kwestionowane) była zalążkiem dzisiejszego symbolu, a w 628 roku Brahmagupta jako pierwszy spisał reguły arytmetyki zera — potraktował je jak pełnoprawną liczbę, nie jak dziurę w zapisie.

Dlaczego maszyna wybrała dwójkę

Wybór dwójki przez komputery nie ma nic wspólnego z elegancją matematyczną. Ma za to wszystko wspólnego z fizyką półprzewodników. Tranzystor w układzie cyfrowym pracuje jako przełącznik sterowany napięciem i rozpoznaje dwa skrajne reżimy: odcięcie (prąd nie płynie — logiczne 0) i nasycenie (prąd płynie swobodnie — logiczna 1).

Kluczowa jest odporność na zakłócenia. Napięcie w realnym układzie faluje: grzeje się krzem, promieniuje sąsiedni przewód, starzeje się materiał. Przy dwóch stanach margines błędu jest ogromny — sygnał może odjechać od wzorca o kilkanaście procent i wciąż zostanie bezbłędnie zaklasyfikowany jako „wysoki" albo „niski". Gdyby procesor liczył dziesiętnie, musiałby rozróżnić dziesięć przedziałów napięcia w tym samym zakresie; każdy z nich byłby dziesięciokrotnie węższy, a układ — dziesięciokrotnie łatwiejszy do oszukania przez szum. Do tego moc wydzielana w układzie rośnie z kwadratem napięcia (P = V²/R), więc „rozpychanie" poziomów, by je od siebie odsunąć, kosztuje ciepło.

Dwójka wygrała nie dlatego, że jest najlepsza, tylko dlatego, że jest najtrudniejsza do zepsucia.

Na papierze wygrywała trójka

Inżynieria zna pojęcie ekonomii bazy (radix economy): koszt reprezentacji liczb w danej podstawie szacuje się jako iloczyn samej podstawy B i liczby potrzebnych pozycji, czyli w przybliżeniu B·log_B(N). Założenie pochodzi z klasycznej pracy High-Speed Computing Devices (1950), gdzie koszt jednej cyfry w bazie B wynosił po prostu B elementów fizycznych.

Jeśli potraktować podstawę jako zmienną ciągłą i poszukać minimum tej funkcji, wynik jest zaskakujący: optimum wypada dokładnie na liczbie e ≈ 2,718. Ponieważ realna maszyna potrzebuje podstawy całkowitej, najbliższym sensownym kandydatem jest trójka, nie dwójka.

I ktoś to zbudował. W 1958 roku na Uniwersytecie Moskiewskim zespół Nikołaja Brusencowa uruchomił Setuń — komputer trójkowy pracujący w systemie zrównoważonym, na stanach −1, 0 i +1. Zaleta była realna: skoro cyfra sama nosi znak, maszyna nie potrzebuje osobnego bitu znaku, a odejmowanie jest po prostu dodawaniem liczby przeciwnej. Powstało kilkadziesiąt egzemplarzy, maszyna działała i była lubiana przez użytkowników.

Przegrała mimo to — i nie z matematyką, lecz z ekonomią skali. Masowa produkcja dwustanowych tranzystorów krzemowych zbiła ich cenę tak drastycznie, że teoretyczna oszczędność trójki przestała cokolwiek znaczyć. To klasyczna historia technologii: wygrywa nie optimum, tylko to, co da się tanio powielić miliard razy.

Leibniz, heksagramy i medal na cześć stworzenia świata

Sam system dwójkowy jest starszy od krzemu o trzy stulecia. Pracował nad nim Thomas Harriot, a rozwinął go Gottfried Wilhelm Leibniz — w manuskrypcie De Progressione Dyadica z marca 1679 roku opisał arytmetykę opartą wyłącznie na 0 i 1.

Dla Leibniza była to jednak przede wszystkim teologia. Jedynkę utożsamiał z Bogiem, zero z nicością i twierdził, że skoro z tych dwóch znaków można wygenerować każdą liczbę, to mamy przed sobą matematyczny obraz stworzenia świata z niczego. Pomysł traktował na tyle serio, że wraz z księciem Rudolfem Augustem zaprojektował pamiątkowy medal Imago Creationis, z tabelą binarną w roli ilustracji aktu stworzenia.

Puenta przyszła z Chin. Jezuita Joachim Bouvet, misjonarz na dworze cesarza Kangxi, wysłał Leibnizowi list datowany na 4 listopada 1701 roku (dotarł dopiero wiosną 1703), a w nim diagram 64 heksagramów Fu Xi z Księgi Przemian. Każdy heksagram to sześć linii w dwóch stanach: przerwana yin i ciągła yang. Ułożone w odpowiednim porządku dają idealną sekwencję liczb od 0 do 63 — sześciobitowy licznik, tyle że w chińskiej szacie i czytany w drugą stronę. Jeszcze w 1703 roku Leibniz opublikował Explication de l'arithmétique binaire.

Szesnastkowy to interfejs dla człowieka

Maszyna czyta bity bezbłędnie. Człowiek — nie. Ciąg 11111111 jest jeszcze do ogarnięcia, ale przy adresie pamięci lub zrzucie z debuggera wzrok gubi się po trzech sekundach. Potrzebny był zapis zwarty, który nie zrywa jednak związku z bitami.

Rozwiązanie wynika wprost z arytmetyki: 8 = 2³, a 16 = 2⁴. Dzięki temu konwersja nie wymaga liczenia — wystarczy pociąć ciąg bitów na kawałki:

  • Szesnastkowy grupuje bity po cztery (taka czwórka to nibble, półbajt). Każda czwórka to dokładnie jeden znak z zakresu 0–9 i A–F, więc bajt to zawsze równe dwa znaki: 11111111FF.
  • Ósemkowy grupuje bity po trzy. Swoją świetność przeżył na maszynach, których długość słowa dzieliła się przez trzy — 12-bitowym PDP-8 czy 36-bitowym PDP-10. Co ciekawe, na 16-bitowym PDP-11 ósemkowy też się utrzymał, mimo że 16 nie dzieli się przez 3: tam sens brały nie słowa, lecz pola instrukcji, rozpisane właśnie na trójki bitów.

Hex nie jest więc „ładniejszym" zapisem. Jest zapisem, który da się w głowie rozpakować z powrotem na bity — i to jest cała jego wartość.

Jak policzyć to na kartce

Konwersja z systemu dziesiętnego na dowolny inny sprowadza się do dzielenia z resztą: dziel liczbę przez docelową podstawę, notuj reszty, powtarzaj z ilorazem, aż dojdziesz do zera. Reszty czytane od ostatniej do pierwszej dają gotowy zapis.

Weźmy 255. Przy podstawie 16 wychodzi 255 ÷ 16 = 15 reszty 15, a potem 15 ÷ 16 = 0 reszty 15. Obie reszty to piętnastka, czyli cyfra F — stąd FF. Przy podstawie 8: 255 ÷ 8 = 31 reszty 7, 31 ÷ 8 = 3 reszty 7, 3 ÷ 8 = 0 reszty 3, czyli 377. Przy podstawie 2 dostajemy osiem jedynek pod rząd. Wszystkie cztery zapisy opisują dokładnie tę samą wartość:

255₁₀ = 0b11111111 = 0o377 = 0xFF

Jeśli nie chcesz robić tego ręcznie, konwerter systemów liczbowych pokazuje wszystkie cztery podstawy naraz i przyjmuje liczby dowolnej wielkości — liczy na arytmetyce dowolnej precyzji, więc nic się po drodze nie zaokrągla.

Gdzie spotkasz to naprawdę

Szesnastkowy i ósemkowy nie są ozdobnikami dla teoretyków. Siedzą w narzędziach, których używasz codziennie.

Kolory. W modelu RGB każdy z trzech kanałów mieści się w jednym bajcie, czyli w zakresie 0–255 — a to dokładnie dwa znaki hex. Dlatego zapis #RRGGBB ma sześć znaków: #FFFFFF to biel (255, 255, 255), #000000 czerń, a #00FF00 czysta zieleń z kanałem G wykręconym na maksimum.

Adresy. Adresy pamięci (0x002F7C1A) i adresy MAC kart sieciowych zapisuje się w hex, bo pojedynczy znak odpowiada czterem liniom adresowym. MAC to 48 bitów, czyli sześć par znaków: 00:1A:2B:3C:4D:5E.

Uprawnienia w Uniksie. Tu rządzi ósemkowy — i to nie przypadkiem. Prawa do pliku dzielą się na trzy grupy (właściciel, grupa, pozostali), a w każdej są trzy niezależne bity: odczyt, zapis, wykonanie. Trzy bity to dokładnie jedna cyfra ósemkowa.

CyfraBityZapisCo oznacza
7111rwxodczyt, zapis i uruchamianie — pełne prawa
6110rw-odczyt i zapis, bez uruchamiania
5101r-xodczyt i uruchamianie, bez modyfikacji
4100r--tylko odczyt
0000---brak jakichkolwiek praw

Kiedy więc wpisujesz chmod 755 skrypt.sh, nie recytujesz magicznej formuły: dajesz właścicielowi 7 (4+2+1 = rwx), a grupie i reszcie świata 5 (4+1 = r-x). Trzy cyfry, dziewięć bitów, zero niejednoznaczności.

Pułapka wiodącego zera

Skoro te same cyfry znaczą co innego w różnych podstawach, kompilator musi jakoś odróżnić 10 binarne od dziesiętnego. Historia tych oznaczeń to małe muzeum decyzji projektowych — i źródło jednego z najbardziej podstępnych błędów w programowaniu.

JęzykÓsemkowySzesnastkowySkąd taka decyzja
BCPL (lata 60.)##xPierwsza próba jednoznacznej składni.
B (Thompson)0brakZero na starcie mówiło parserowi: to liczba, nie nazwa zmiennej.
C (Ritchie)0 (np. 010)0x (np. 0x1A)Wiodące zero zostało po B; 0x przyszło z BCPL.
Nowoczesne (ES6)0o0x, 0bJawny prefiks, żeby ukrócić dwuznaczność wiodącego zera.

Ta jedna decyzja Ritchiego — zachowanie wiodącego zera jako znacznika ósemkowego — mści się do dziś. W C, C++, Javie, Ruby czy Pythonie 2 zapis 010 nie jest dziesiątką, tylko ósemką (1·8¹ + 0·8⁰ = 8). Konwersja zachodzi na etapie tokenizacji, zanim program w ogóle ruszy, więc kompilator nawet nie mrugnie.

Brzmi niewinnie, dopóki nie wyrównasz kolumny liczb wiodącymi zerami albo nie wpiszesz do kodu kodu pocztowego, numeru konta czy identyfikatora produktu zaczynającego się od zera. Wtedy jedna wartość po cichu przestaje być tą, którą widzisz na ekranie.

Języki zdążyły się przed tym zabezpieczyć. Python 3 wymaga jawnego 0o i odrzuca samo 010 jako błąd składni. Nowoczesny JavaScript w trybie ścisłym ('use strict') potraktuje stary literał ósemkowy jak SyntaxError, zamiast po cichu podmienić wartość, a od ES6 ma bezpieczne prefiksy 0o i 0b. Warto przy okazji rozbroić popularny mit: parseInt('010') w dzisiejszych silnikach zwraca 10, nie 8 — automatyczne wykrywanie ósemki wycięto z języka lata temu. Nawyk pisania parseInt('010', 10) i tak jest dobry, ale nie dlatego, że silnik nadal zgaduje.

Tabela równoważności

DecBinOctHexDlaczego akurat ta wartość
00b00o00x0Tranzystor w odcięciu
10b10o10x1Stan wysoki — pojedynczy bit
70b1110o70x7Największa cyfra ósemkowa; rwx
80b10000o100x8Liczba bitów w bajcie
100b10100o120xAPierwsza „litera" w hex
150b11110o170xFMaksimum półbajtu (nibble)
160b100000o200x10Podstawa hex — tu zapis się „przekręca"
640b10000000o1000x40Liczba heksagramów Księgi Przemian (2⁶)
1270b11111110o1770x7FGranica 7-bitowego ASCII
1280b100000000o2000x80Pierwsza wartość z ustawionym najwyższym bitem
2550b111111110o3770xFFMaksimum jednego bajtu
2560b1000000000o4000x100Liczba wszystkich stanów bajtu (2⁸)

Cztery zapisy, jedna liczba

Systemy liczbowe to najczystszy przykład tego, jak technologia negocjuje między fizyką a biologią. Krzem narzucił dwójkę, bo dwa stany są jedynym, czego tranzystor nie potrafi pomylić. Człowiek odpowiedział szesnastką, bo cztery bity naraz jeszcze mieszczą mu się w głowie. Ósemkowy przetrwał w uniksowych uprawnieniach, gdzie trójki bitów układają się w prawa dostępu. Dziesiętny został tam, gdzie zawsze był — na naszych dłoniach.

Żaden z tych systemów nie jest prawdziwszy od pozostałych. 0xFF, 0o377, 0b11111111 i 255 to cztery sposoby wskazania tej samej liczby — a jedyne, co naprawdę trzeba wiedzieć, to w którym z nich akurat się mówi.

Dalsza lektura

Wypróbuj

Konwerter — Systemy liczbowe

Otwórz konwerter