INF.04 · C# · lekcja 2

Systemy liczbowe — dwójkowy, ósemkowy, szesnastkowy i kod U2

Odczytaj bity, a potem zapisz własną liczbę

  • Zrozum
  • Sprawdź
  • Napisz sam

Po tej lekcji: odczytaj bity, a potem zapisz własną liczbę

Dzisiaj zrobisz konkretną rzecz

Komputer "myśli" wyłącznie w zerach i jedynkach — każda liczba, znak i instrukcja w programie jest w pamięci zapisana binarnie. Bez zrozumienia systemów liczbowych trudno pojąć, dlaczego int ma ograniczony zakres, czym różni się liczba ujemna od dodatniej "w środku" komputera, ani jak działają operacje bitowe. To wymaganie egzaminacyjne (INF.04.2.5) i solidny fundament pod resztę kursu C#.

Kolory w CSS/HTML zapisujesz szesnastkowo (#3498DB) — dwie cyfry hex na kanał R, G, B (0-255 każdy). Adresy MAC kart sieciowych, klucze szyfrujące, sumy kontrolne plików (hash SHA-256) — wszystko to zapis szesnastkowy, bo jest zwarty i czytelny dla człowieka, a jednocześnie 1:1 odpowiada bitom w pamięci.

Sprawdź w działaniu

Odczytaj bity, a potem zapisz własną liczbę

Najpierw przewidź wynik. Zmień dane w modelu i porównaj efekt. Spróbuj także przypadku granicznego, a nie tylko pierwszego poprawnego przykładu.

Ta interakcja wymaga włączonego JavaScriptu.

Model pokazuje wybraną zasadę. Nie uruchamia ani nie ocenia wklejonego kodu C#.

1. Przygotuj i uruchom własny projekt

2. Najpierw mały przykład

Wykonaj tę próbę przed dłuższym rozwiązaniem. Convert.ToInt32 otrzymuje napis i podstawę systemu. 2 oznacza zapis dwójkowy, nie liczbę cyfr. ToString("X") formatuje liczbę szesnastkowo. Wartość pozostaje ta sama.

Program.cscsharp
int liczba = Convert.ToInt32("1011", 2);
Console.WriteLine(liczba);
Console.WriteLine(Convert.ToString(liczba, 2));
Console.WriteLine(liczba.ToString("X"));
Wynik
11
1011
B
  1. Co sprawdzić? Zmień jedną wartość i przed uruchomieniem zapisz przewidywany rezultat. Porównaj go z opisem wyniku; przy błędzie wróć do konkretnej instrukcji.

Jak to działa — 1

System pozycyjny — wartość cyfry zależy od jej POZYCJI w liczbie, nie tylko od samego symbolu. W systemie dziesiętnym (podstawa 10, cyfry 0-9), z którego korzystamy na co dzień, liczba 245 to tak naprawdę 2·10² + 4·10¹ + 5·10⁰ = 200 + 40 + 5.

System dwójkowy (binarny, podstawa 2) — używa tylko cyfr 0 i 1. Każda pozycja to kolejna potęga liczby 2 (licząc od prawej: 1, 2, 4, 8, 16, 32...). Przykład: 1011₂ = 1·2³ + 0·2² + 1·2¹ + 1·2⁰ = 8 + 0 + 2 + 1 = 11₁₀.

Zamiana dziesiętny → binarny — dzielimy liczbę kolejno przez 2, zapisując reszty z dzielenia od DOŁU do GÓRY: 11 : 2 = 5 reszta 1; 5 : 2 = 2 reszta 1; 2 : 2 = 1 reszta 0; 1 : 2 = 0 reszta 1. Czytając reszty od ostatniej do pierwszej: 1011.

System ósemkowy (oktalny, podstawa 8) — cyfry 0-7, każda pozycja to potęga 8. Rzadziej używany dziś w praktyce, ale nadal spotykany (np. uprawnienia plików w Uniksie: chmod 755).

Jak to działa — 2

System szesnastkowy (heksadecymalny, podstawa 16) — cyfry 0-9 oraz A-F (gdzie A=10, B=11, C=12, D=13, E=14, F=15). Bardzo popularny w programowaniu, bo JEDNA cyfra szesnastkowa odpowiada DOKŁADNIE czterem bitom — dzięki temu zapis jest krótszy niż binarny, a przeliczenie proste. Przykłady zastosowań: kolory w CSS/HTML (#FF5733), adresy pamięci, zrzuty pamięci (hex dump). W C# liczby szesnastkowe zapisuje się z przedrostkiem 0x, np. 0xFF = 255.

Kod uzupełnieniowy do dwóch (U2) — sposób zapisu liczb UJEMNYCH w pamięci komputera. Bez niego procesor musiałby mieć osobną logikę dla dodawania i odejmowania — dzięki U2 odejmowanie to po prostu dodawanie liczby ujemnej, a sprzęt potrzebuje TYLKO jednego układu dodającego. Zasada tworzenia liczby ujemnej w U2 (dla 8 bitów): 1) zapisz liczbę dodatnią binarnie, 2) zneguj wszystkie bity (0→1, 1→0) — to tzw. kod uzupełnieniowy do jedynki (U1), 3) dodaj 1. Przykład dla -5 na 8 bitach: 5 = 00000101 → negacja → 11111010 → +1 → 11111011 = -5 w U2. Najstarszy (skrajnie lewy) bit pełni rolę bitu znaku: 0 = liczba dodatnia, 1 = liczba ujemna.

Jak to działa — 3

Dlaczego to działa — dodając liczbę i jej reprezentację U2 otrzymujemy zawsze zero (z pominięciem nadmiarowego bitu przeniesienia), np. 00000101 (5) + 11111011 (-5) = 100000000 → obcinając nadmiarowy 9. bit zostaje 00000000 = 0. Dokładnie to dzieje się "pod maską" w typie int w C#, gdy piszesz 5 + (-5).

Operacje bitowe — działania logiczne wykonywane bezpośrednio na pojedynczych bitach liczby: & (AND — 1 tylko gdy oba bity są 1), | (OR — 1 gdy choć jeden bit jest 1), ^ (XOR — 1 gdy bity są RÓŻNE), ~ (negacja bitowa — odwraca każdy bit), <</>> (przesunięcie bitowe w lewo/prawo — odpowiednik mnożenia/dzielenia przez potęgę 2).

3. Pełny przykład — czytaj od Main

Teraz zastąp Program.cs tym programem. Zanim uruchomisz, znajdź dane wejściowe, operację i miejsce prezentacji wyniku. W tym wariancie zachowujemy jawny Main. Nie wklejaj kilku pełnych programów do jednego pliku.

Program.cs — osobne uruchomieniecsharp
using System;

class SystemyLiczbowe
{
    static void Main()
    {
        int liczba = 245;

        // Konwersje wbudowane w .NET
        string binarnie = Convert.ToString(liczba, 2);
        string osemkowo = Convert.ToString(liczba, 8);
        string szesnastkowo = Convert.ToString(liczba, 16);

        Console.WriteLine($"Dziesiętnie:   {liczba}");
        Console.WriteLine($"Binarnie:      {binarnie}");
        Console.WriteLine($"Ósemkowo:      {osemkowo}");
        Console.WriteLine($"Szesnastkowo:  {szesnastkowo.ToUpper()}");

        // Zapis literału szesnastkowego wprost w kodzie
        int kolorCzerwony = 0xFF0000;
        Console.WriteLine($"0xFF0000 jako liczba dziesiętna: {kolorCzerwony}");

        // Odczyt binarny -> int
        int wynik = Convert.ToInt32("1011", 2);
        Console.WriteLine($"1011 (binarnie) = {wynik} (dziesiętnie)");

        // Operacje bitowe
        int a = 12;  // 1100
        int b = 10;  // 1010
        Console.WriteLine($"a & b = {a & b}");   // 1000 = 8
        Console.WriteLine($"a | b = {a | b}");   // 1110 = 14
        Console.WriteLine($"a ^ b = {a ^ b}");   // 0110 = 6
        Console.WriteLine($"a << 1 = {a << 1}"); // 11000 = 24 (mnożenie przez 2)
        Console.WriteLine($"a >> 1 = {a >> 1}"); // 0110 = 6 (dzielenie całkowite przez 2)
    }
}
  1. Prześledź kod Convert.ToString(liczba, podstawa) to gotowa funkcja biblioteczna .NET zamieniająca liczbę dziesiętną na zapis w innej podstawie (2, 8 lub 16) — w praktyce programista rzadko liczy to ręcznie, ale ZROZUMIENIE mechanizmu (dzielenie z resztą) jest wymagane na egzaminie teoretycznym. 0xFF0000 to literał szesnastkowy — kompilator C# rozpoznaje przedrostek 0x i sam przelicza wartość na liczbę całkowitą, dokładnie tak jak zwykłe liczby dziesiętne. a << 1 (przesunięcie bitowe w lewo o 1) jest RÓWNOWAŻNE pomnożeniu przez 2 — a a >> 1 dzieleniu całkowitemu przez 2. Procesory wykonują przesunięcia bitowe znacznie szybciej niż mnożenie/dzielenie, więc bywają używane do optymalizacji (choć kompilator często robi to automatycznie).

Sprawdź, zanim przepiszesz cały program

Mylenie kolejności reszt przy ręcznej zamianie dziesiętny→binarny — reszty z kolejnych dzieleń trzeba odczytać OD OSTATNIEJ DO PIERWSZEJ (od dołu do góry), a nie w kolejności, w jakiej zostały policzone.

Zapominanie o bicie znaku w U2 — najstarszy bit w kodzie U2 NIE jest zwykłą cyfrą wagi 2ⁿ⁻¹, tylko wskazuje znak liczby (0=dodatnia, 1=ujemna); traktowanie go jak zwykłego bitu przy ręcznym liczeniu wartości prowadzi do błędnego wyniku.

**Mylenie operatorów bitowych &/| z operatorami logicznymi &&/||** — te pierwsze działają na POJEDYNCZYCH BITACH liczby, te drugie na wartościach logicznych bool (prawda/fałsz) w warunkach if. Użycie & zamiast && w warunku zwykle nadal "działa" (bo C# potrafi niejawnie to obsłużyć dla typu bool), ale to zupełnie inna, mniej czytelna operacja.

Ćwiczenie z pomocą · samodzielna praca

4. Teraz Twoja zmiana

Napisz program, który wczytuje liczbę dziesiętną od użytkownika i wypisuje jej odpowiedniki binarny, ósemkowy i szesnastkowy (użyj Convert.ToString). Następnie ręcznie (na kartce) zamień 3 wybrane liczby dziesiętne na binarne metodą dzielenia przez 2 i porównaj z wynikiem programu.

Oczekiwany rezultat
Spełnij warunki polecenia. Zapisz wejście, otrzymany wynik i wyjaśnij, która część kodu za niego odpowiada.
Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź

Wróć do podstawowej formy oraz objaśnienia: Convert.ToInt32 otrzymuje napis i podstawę systemu. 2 oznacza zapis dwójkowy, nie liczbę cyfr. ToString("X") formatuje liczbę szesnastkowo. Wartość pozostaje ta sama.

Krok 2 — pokaż podpowiedź

Zmień tylko część potrzebną do polecenia. Zachowaj działającą wersję przed zmianą i uruchamiaj po każdym małym etapie.

Krok 3 — pokaż podpowiedź

Sprawdź przypadek zwykły i graniczny; porównaj z regułą: 11 1011 B

Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.

5. Sprawdź rozumienie, nie tylko uruchomienie

Dalej na swoim koncie

Rozgrzewki, quiz i zadania są w biurku ucznia.

Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.

Zaloguj się →
Źródła i dalsza nauka