Liczby ze znakiem — kod U2

🎯 Po co Ci to?

W 2014 roku licznik wyświetleń pewnego teledysku w serwisie wideo „przekręcił się" — piosenka „Gangnam Style" przekroczyła 2 147 483 647 odtworzeń i serwis musiał w pośpiechu zmieniać reprezentację licznika. Skąd ta dziwna granica? To $2^{31} - 1$ — największa liczba mieszcząca się w 32 bitach, gdy jeden bit oddasz na znak. Dziś zobaczysz, jak komputer zapisuje liczby ujemne — i dlaczego wybrał sposób, który na pierwszy rzut oka wygląda na szaleństwo, a jest czystym sprytem.

✅ Czego się nauczysz

Po tej jednostce potrafisz:

  • zapisać liczbę ujemną w kodzie uzupełnieniowym do dwóch (U2) i odczytać ją z powrotem;
  • wykonać pisemne dodawanie i odejmowanie w systemie dwójkowym (i innych);
  • wyjaśnić zjawisko przepełnienia i wskazać jego skutki w prawdziwym świecie.

📘 Wyjaśnienie

Najpierw rachunki bez znaku. Pisemne dodawanie binarne działa jak dziesiętne z podstawówki, tylko „dziesiątka" nadchodzi szybciej: $1 + 1 = 10_2$, czyli zero i jeden dalej (przeniesienie).

   1 1 1        ← przeniesienia
   0 1 1 0 1    (13)
 + 0 0 1 1 1    ( 7)
 ─────────────
   1 0 1 0 0    (20)

Sprawdź kolumnami od prawej: $1+1 = 0$ i przeniesienie; $0+1+1 = 0$ i przeniesienie; $1+1+1 = 1$ i przeniesienie... Ten sam mechanizm działa w każdym systemie pozycyjnym — w szesnastkowym przeniesienie pojawia się przy szesnastu, w ósemkowym przy ośmiu.

Jak zapisać minus? Pomysł naiwny: pierwszy bit niech oznacza znak (0 = plus, 1 = minus), reszta wartość. Prosty — i wadliwy: zero ma wtedy dwa zapisy ($0000$ i $1000$), a sprzęt musiałby mieć osobne układy do dodawania i odejmowania. Inżynierowie wybrali sprytniej.

📐 DEFINICJA — kod uzupełnieniowy do dwóch (U2): na $n$ bitach najstarszy bit ma wagę ujemną $-2^{n-1}$, pozostałe zwykłe dodatnie. Zapis $b_{n-1}b_{n-2}\dots b_0$ oznacza $-b_{n-1} \cdot 2^{n-1} + b_{n-2} \cdot 2^{n-2} + \dots + b_0$.

Po ludzku: pierwsza szufladka bajta „jest winna" 128, pozostałe normalnie dokładają. $10000000_2 = -128$, $11111111_2 = -128 + 127 = -1$. Czym NIE jest: zapisem „znak plus wartość". W U2 nie da się osobno przeczytać znaku i modułu — trzeba policzyć całość.

Na 8 bitach U2 mieści liczby od $-128$ do $127$. Zapis liczby ujemnej najszybciej znajdziesz przepisem: zaneguj wszystkie bity i dodaj 1. Dla $-7$: bierzesz $7 = 00000111$, negujesz → $11111000$, dodajesz 1 → $11111001$. Kontrola: $-128 + 64 + 32 + 16 + 8 + 1 = -7$. ✓

Po co to szaleństwo? Bo teraz odejmowanie staje się dodawaniem. Chcesz policzyć $13 - 7$? Dodaj do 13 zapis liczby $-7$:

   0 0 0 0 1 1 0 1    ( 13)
 + 1 1 1 1 1 0 0 1    ( −7)
 ─────────────────
 1 0 0 0 0 0 1 1 0    →  odrzuć przeniesienie poza 8 bitów: 00000110 = 6 ✓

Jeden układ elektroniczny załatwia dodawanie i odejmowanie. Właśnie za takie pomysły inżynierom stawia się pomniki (a przynajmniej powinno się).

🧮 Prześledź

Odczytaj wartości zapisów U2 na 8 bitach: $01100100$, $10011100$, $11110110$. (Pamiętaj: pierwszy bit ma wagę $-128$.)

Sprawdź odpowiedź

$01100100 = 64+32+4 = 100$. $10011100 = -128+16+8+4 = -100$ (zauważ: to negacja pierwszego plus jeden — pary $x$ i $-x$ zawsze tak wyglądają). $11110110 = -128+64+32+16+4+2 = -10$. Szybki test znaku: pierwszy bit 1 → liczba ujemna, zawsze.

Przepełnienie. Zakres U2 jest skończony — a liczby na osi nie. Co się stanie, gdy do $127$ (czyli $01111111$) dodasz 1? Wyjdzie $10000000$, czyli… $-128$. Licznik „przekręca się" jak drogomierz w starym aucie. To zjawisko nazywa się przepełnieniem (ang. overflow) i nie jest teorią: wspomniany licznik odtworzeń, gliczowe „ujemne pieniądze" w grach, a w 2038 roku problem czeka systemy zapisujące czas w 32 bitach ze znakiem. Programista rozszerzenia musi wiedzieć, na ilu bitach żyją jego liczby.

⚠️ Uwaga, pułapka

Zapis U2 ma sens tylko razem z liczbą bitów. Ciąg $1001$ to na 4 bitach $-7$, ale na 8 bitach ($00001001$) — zwykłe 9. Na sprawdzianie zawsze upewnij się, ile bitów obowiązuje w zadaniu; bez tego odpowiedź jest loterią.

🛠️ Teraz Ty

Bez komputera: zapisz w U2 na 8 bitach liczby $-1$, $-64$, $-100$ i sprawdź każdą, sumując wagi. Potem policz pisemnie $01010110_2 + 00011011_2$ oraz — w systemie szesnastkowym, z przeniesieniami — $3A_{16} + 2B_{16}$. Z komputerem: w Pythonie sprawdzisz drugie działanie przez hex(0x3A + 0x2B).

📐 Definicje tej lekcji

  • Kod U2 — zapis liczb ze znakiem, w którym najstarszy bit ma wagę ujemną; standard we współczesnych procesorach.
  • Przepełnienie — przekroczenie zakresu reprezentacji; wynik „przekręca się" na drugi koniec zakresu.

📌 Najważniejsze w pigułce

  • Pisemne rachunki działają w każdym systemie — zmienia się tylko moment przeniesienia.
  • U2: pierwszy bit „winien" $2^{n-1}$; negacja to „odwróć bity i dodaj 1"; odejmowanie staje się dodawaniem.
  • Zakres jest skończony: 8 bitów to $-128 \dots 127$; poza nim czai się przepełnienie.

🎒 Zadania

  1. Policz pisemnie w systemie dwójkowym: $10110_2 + 1110_2$ oraz $110100_2 - 10011_2$ (odejmowanie wykonaj przez dodanie liczby przeciwnej w U2 na 8 bitach).
Wskazówka i odpowiedź

Dodawanie: $10110_2 + 01110_2 = 100100_2$ (22 + 14 = 36 ✓). Odejmowanie: $52 - 19$; $-19$ w U2: $19 = 00010011$ → negacja $11101100$ → $+1$ = $11101101$; $00110100 + 11101101 = (1)00100001$ → odrzuć dziewiąty bit → $00100001_2 = 33$ ✓.

  1. Gra zapisuje liczbę monet gracza na 16 bitach bez znaku. Gracz ma 65 530 monet i wygrywa jeszcze 10. Ile monet pokaże gra i dlaczego?
Wskazówka i odpowiedź

Zakres 16 bitów bez znaku to $0 \dots 65535$. $65530 + 10 = 65540$, czyli o 5 za dużo — licznik przekręca się do 4 ($65540 - 65536$). Gracz „traci" fortunę przez reprezentację, nie przez grę. Takie błędy naprawdę zdarzały się w grach i sklepach internetowych; niektóre pozwalały wręcz „kupić" ujemną liczbę sztuk towaru.

  1. Uzasadnij, że w U2 na $n$ bitach liczb ujemnych jest o jedną więcej niż dodatnich.
Wskazówka i odpowiedź

Kombinacji jest $2^n$. Jedna to zero. Zapisy z pierwszym bitem 1 (połowa, czyli $2^{n-1}$) to liczby ujemne: od $-1$ do $-2^{n-1}$. Zostaje $2^{n-1} - 1$ dodatnich (od 1 do $2^{n-1}-1$). Ujemnych jest więc o jedną więcej — dlatego zakres bajta to niesymetryczne $-128 \dots 127$ i dlatego $-(-128)$ potrafi w programach eksplodować: $+128$ nie istnieje na 8 bitach.

🔍 Sprawdź, czy umiesz

  • Zapisać dowolną liczbę z zakresu $-128 \dots 127$ w U2 i odczytać ją z powrotem.
  • Wykonać pisemne dodawanie w systemach 2 i 16.
  • Opowiedzieć historię z życia, w której przepełnienie narobiło szkód.

Ucz się tej jednostki z asystentem