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:
11111111→FF. - Ó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.
| Cyfra | Bity | Zapis | Co oznacza |
|---|---|---|---|
| 7 | 111 | rwx | odczyt, zapis i uruchamianie — pełne prawa |
| 6 | 110 | rw- | odczyt i zapis, bez uruchamiania |
| 5 | 101 | r-x | odczyt i uruchamianie, bez modyfikacji |
| 4 | 100 | r-- | tylko odczyt |
| 0 | 000 | --- | 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 | Ósemkowy | Szesnastkowy | Skąd taka decyzja |
|---|---|---|---|
| BCPL (lata 60.) | # | #x | Pierwsza próba jednoznacznej składni. |
| B (Thompson) | 0 | brak | Zero 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) | 0o | 0x, 0b | Jawny 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
| Dec | Bin | Oct | Hex | Dlaczego akurat ta wartość |
|---|---|---|---|---|
| 0 | 0b0 | 0o0 | 0x0 | Tranzystor w odcięciu |
| 1 | 0b1 | 0o1 | 0x1 | Stan wysoki — pojedynczy bit |
| 7 | 0b111 | 0o7 | 0x7 | Największa cyfra ósemkowa; rwx |
| 8 | 0b1000 | 0o10 | 0x8 | Liczba bitów w bajcie |
| 10 | 0b1010 | 0o12 | 0xA | Pierwsza „litera" w hex |
| 15 | 0b1111 | 0o17 | 0xF | Maksimum półbajtu (nibble) |
| 16 | 0b10000 | 0o20 | 0x10 | Podstawa hex — tu zapis się „przekręca" |
| 64 | 0b1000000 | 0o100 | 0x40 | Liczba heksagramów Księgi Przemian (2⁶) |
| 127 | 0b1111111 | 0o177 | 0x7F | Granica 7-bitowego ASCII |
| 128 | 0b10000000 | 0o200 | 0x80 | Pierwsza wartość z ustawionym najwyższym bitem |
| 255 | 0b11111111 | 0o377 | 0xFF | Maksimum jednego bajtu |
| 256 | 0b100000000 | 0o400 | 0x100 | Liczba 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
- Brian Hayes, Third Base, American Scientist — ekonomia bazy liczbowej i radzieckie komputery trójkowe.
- Uniwersytet Oksfordzki, datowanie radiowęglowe manuskryptu z Bakhshali — najstarszy znany zapis symbolu zera.
- G. W. Leibniz, Explication de l'arithmétique binaire (1703) — źródłowy tekst o systemie dwójkowym i heksagramach I Ching.
- MDN, SyntaxError: "0"-prefixed octal literals are deprecated — jak nowoczesny JavaScript rozbraja pułapkę wiodącego zera.
