5.3 — Systemy liczbowe (dziesiętny, binarny, szesnastkowy i ósemkowy)

Nota autora

Ta lekcja jest opcjonalna.

Przyszłe lekcje odnoszą się do liczb szesnastkowych, dlatego przed kontynuowaniem powinieneś przynajmniej choć trochę zapoznać się z tą koncepcją.

W życiu codziennym liczymy liczbami dziesiętnymi liczby, gdzie każda cyfra może wynosić 0, 1, 2, 3, 4, 5, 6, 7, 8 lub 9. Liczba dziesiętna jest również nazywana „o podstawie 10”, ponieważ istnieje 10 możliwych cyfr (od 0 do 9). W tym systemie liczymy tak: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,… Domyślnie w programach C++ przyjmuje się, że liczby są dziesiętne.

int x { 12 }; // 12 is assumed to be a decimal number

W binarny, są tylko 2 cyfry: 0 i 1, więc nazywa się to „podstawą 2”. W systemie binarnym liczymy w ten sposób: 0, 1, 10, 11, 100, 101, 110, 111,… Aby ułatwić ich odczytanie, dłuższe liczby binarne są często oddzielane spacjami na grupy po 4 cyfry (np. 1101 0100).

Dziesiętny i binarny to dwa przykłady systemów liczbowych, co jest fantazyjną nazwą dla zbioru symboli (np. cyfr) używanych do reprezentowania liczb. W C++ dostępne są 4 główne systemy liczbowe. Według popularności są to: dziesiętny (podstawa 10), binarny (podstawa 2), szesnastkowy (podstawa 16) i ósemkowy (podstawa 8).

Nomenklatura

Zarówno w systemie dziesiętnym, jak i binarnym liczby 0 i 1 mają to samo znaczenie. W obu systemach nazywamy te liczby „zero” i „jeden”.

Ale co z liczbą 10? 10 to liczba występująca po ostatniej jednocyfrowej liczbie w systemie liczbowym. W systemie dziesiętnym 10 jest równe dziewięć plus jeden. Nazywamy tę liczbę „dziesięć”.

W systemie binarnym 10 używa tych samych cyfr, ale jest równy jeden plus jeden (odpowiednik dwóch w systemie dziesiętnym). Nazywanie systemu binarnego 10 „dziesięcią” byłoby mylące, ponieważ „dziesięć” to dziewięć plus jeden, a to 10 jest jeden plus jeden.

Z tego powodu nazwy „dziesięć”, „jedenaście”, „dwanaście” itp. są zwykle zarezerwowane dla liczb dziesiętnych. W niedziesiętnych systemach liczbowych wolimy nazywać te liczby jeden-zero, jeden jeden, jeden-dwa itd. Binarny 101 to nie „sto jeden”, tylko „jeden-zero-jeden”.

Literały ósemkowe i szesnastkowe

Ośmkowy to podstawa 8 - czyli jedyne dostępne cyfry to: 0, 1, 2, 3, 4, 5, 6 i 7. W ósemkowym liczymy w ten sposób: 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, … (uwaga: nr 8 i 9, więc przeskakujemy od 7 do 10).

Dziesiętny01234567891011
Ośmkowy0123456710111213

Aby użyć literału ósemkowego, poprzedź literał 0 (zero):

#include <iostream>

int main()
{
    int x{ 012 }; // 0 before the number means this is octal
    std::cout << x << '\n';
    return 0;
}

Ten program wypisuje:

10

Dlaczego 10 zamiast 12? Ponieważ liczby są domyślnie podawane w postaci dziesiętnej, a 12 ósemkowych = 10 dziesiętnych.

Osemkowy jest rzadko używany i zalecamy go unikać.

Szesnastkowy to podstawa 16. W systemie szesnastkowym liczymy w ten sposób: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, …

Dziesiętny01234567891011121314151617
Szesnastkowy0123456789ABCDEF1011

Możesz także użyć małych liter (chociaż duże są bardziej powszechne).

Aby użyć literału szesnastkowego, poprzedź go 0x:

#include <iostream>

int main()
{
    int x{ 0xF }; // 0x before the number means this is hexadecimal
    std::cout << x << '\n';
    return 0;
}

Ten program wypisuje:

15

Możesz także użyć przedrostka 0X , ale 0x jest konwencjonalny, ponieważ jest łatwiejszy do przeczytania.

Tabele systemów liczbowych

Oto cztery systemy liczbowe ułożone w kolejności, aby ułatwić sprawdzenie postępu w każdym z nich:

Decimal         0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15
Binary          0     1    10    11   100   101   110   111  1000  1001  1010  1011  1100  1101  1110  1111
Octal           0     1     2     3     4     5     6     7    10    11    12    13    14    15    16    17
Hexadecimal     0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F

Decimal        16    17    18    19    20    21    22    23    24    25    26    27    28    29    30    31
Binary      10000 10001 10010 10011 10100 10101 10110 10111 11000 11001 11010 11011 11100 11101 11110 11111
Octal          20    21    22    23    24    25    26    27    30    31    32    33    34    35    36    37
Hexadecimal    10    11    12    13    14    15    16    17    18    19    1A    1B    1C    1D    1E    1F

Każdy z tych wierszy zawiera ten sam wzór: cyfra znajdująca się najbardziej na prawo jest zwiększana od 0 do (podstawa-1). Kiedy cyfra osiągnie (podstawa), jest resetowana do 0, a cyfra po lewej stronie jest zwiększana o 1. Jeśli ta lewa cyfra osiąga (podstawa), jest resetowana do 0, a cyfra na lewo od niej jest zwiększana o 1. I tak dalej…

Używanie zapisu szesnastkowego do reprezentacji binarnej

Ponieważ cyfra szesnastkowa ma 16 różnych wartości, możemy powiedzieć, że pojedynczy cyfra szesnastkowa obejmuje 4 bity. W rezultacie do dokładnego przedstawienia pełnego bajtu można użyć pary cyfr szesnastkowych.

Szesnastkowy0123456789ABCDEF
Binary0000000100100011010001010110011110001001101010111100110111101111

Rozważ 32-bitową liczbę całkowitą o wartości binarnej 0011 1010 0111 1111 1001 1000 0010 0110. Ze względu na długość i powtarzalność cyfr nie jest to łatwe do odczytania. W systemie szesnastkowym ta sama wartość będzie wynosić: 3A7F 9826, co jest znacznie bardziej zwięzłe. Z tego powodu wartości szesnastkowe są często używane do reprezentowania adresów pamięci lub nieprzetworzonych danych w pamięci (których typ nie jest znany).

Literały binarne

Przed C++14 nie było obsługi literałów binarnych. Jednak literały szesnastkowe zapewniają nam przydatne obejście (które nadal możesz zobaczyć w istniejących bazach kodu):

#include <iostream>

int main()
{
    int bin{};    // assume 16-bit ints
    bin = 0x0001; // assign binary 0000 0000 0000 0001 to the variable
    bin = 0x0002; // assign binary 0000 0000 0000 0010 to the variable
    bin = 0x0004; // assign binary 0000 0000 0000 0100 to the variable
    bin = 0x0008; // assign binary 0000 0000 0000 1000 to the variable
    bin = 0x0010; // assign binary 0000 0000 0001 0000 to the variable
    bin = 0x0020; // assign binary 0000 0000 0010 0000 to the variable
    bin = 0x0040; // assign binary 0000 0000 0100 0000 to the variable
    bin = 0x0080; // assign binary 0000 0000 1000 0000 to the variable
    bin = 0x00FF; // assign binary 0000 0000 1111 1111 to the variable
    bin = 0x00B3; // assign binary 0000 0000 1011 0011 to the variable
    bin = 0xF770; // assign binary 1111 0111 0111 0000 to the variable

    return 0;
}

W C++14 i nowszych możemy używać literałów binarnych, używając przedrostka 0b:

#include <iostream>

int main()
{
    int bin{};        // assume 16-bit ints
    bin = 0b1;        // assign binary 0000 0000 0000 0001 to the variable
    bin = 0b11;       // assign binary 0000 0000 0000 0011 to the variable
    bin = 0b1010;     // assign binary 0000 0000 0000 1010 to the variable
    bin = 0b11110000; // assign binary 0000 0000 1111 0000 to the variable

    return 0;
}

Separatory cyfr

Ponieważ długie literały mogą być trudne do odczytania, C++14 dodaje również możliwość używania cudzysłowu (‘) jako cyfry separator.

#include <iostream>

int main()
{
    int bin { 0b1011'0010 };  // assign binary 1011 0010 to the variable
    long value { 2'132'673'462 }; // much easier to read than 2132673462

    return 0;
}

Pamiętaj również, że separator nie może występować przed pierwszą cyfrą wartości:

    int bin { 0b'1011'0010 };  // error: ' used before first digit of value

Separatory cyfr mają charakter wyłącznie wizualny i w żaden sposób nie wpływają na wartość dosłowną.

Wyprowadzanie wartości w postaci dziesiętnej, ósemkowej lub szesnastkowej

Domyślnie C++ wyprowadza wartości w postaci dziesiętnej. Można jednak zmienić format wyjściowy za pomocą std::dec, std::oct, I std::hex manipulatorów we/wy:

#include <iostream>

int main()
{
    int x { 12 };
    std::cout << x << '\n'; // decimal (by default)
    std::cout << std::hex << x << '\n'; // hexadecimal
    std::cout << x << '\n'; // now hexadecimal
    std::cout << std::oct << x << '\n'; // octal
    std::cout << std::dec << x << '\n'; // return to decimal
    std::cout << x << '\n'; // decimal

    return 0;
}

Wypisuje:

12
c
c
14
12
12

Zauważ, że po zastosowaniu manipulator we/wy pozostaje ustawiony na przyszłe dane wyjściowe, dopóki nie zostanie ponownie zmieniony.

Wyprowadzanie wartości binarnych

Wyprowadzanie wartości binarnych jest nieco trudniejsze, ponieważ std::cout nie jest to dostarczane wbudowane możliwości. Na szczęście standardowa biblioteka C++ zawiera typ o nazwie std::bitset , który zrobi to za nas (w nagłówku <bitset>).

Aby użyć std::bitset możemy zdefiniować std::bitset zmienną i powiedzieć std::bitset ile bitów chcemy przechowywać. Liczba bitów musi być stałą czasową kompilacji. std::bitset można zainicjować wartością całkowitą (w dowolnym formacie, w tym dziesiętnym, ósemkowym, szesnastkowym lub binarnym).

#include <bitset> // for std::bitset
#include <iostream>

int main()
{
	// std::bitset<8> means we want to store 8 bits
	std::bitset<8> bin1{ 0b1100'0101 }; // binary literal for binary 1100 0101
	std::bitset<8> bin2{ 0xC5 }; // hexadecimal literal for binary 1100 0101

	std::cout << bin1 << '\n' << bin2 << '\n';
	std::cout << std::bitset<4>{ 0b1010 } << '\n'; // create a temporary std::bitset and print it

	return 0;
}

Wypisuje:

11000101
11000101
1010

W powyższym kodzie ta linia:

std::cout << std::bitset<4>{ 0b1010 } << '\n'; // create a temporary std::bitset and print it

tworzy tymczasowy (nienazwany) std::bitset obiekt z 4 bitami, inicjuje go binarnie literal 0b1010, wypisuje wartość w formacie binarnym, a następnie odrzuca obiekt tymczasowy.

Powiązana treść

Szczegółowo omówimy std::bitset w lekcji O.1 — Flagi bitowe i manipulacja bitami za pomocą std::bitset.

Wyprowadzanie wartości w formacie binarnym przy użyciu biblioteki Format/Drukuj Zaawansowane

W C++20 i C++23 mamy lepsze opcje drukowania binarnego za pośrednictwem nowej Biblioteki formatów (C++20) i Biblioteki drukowania (C++23):

#include <format> // C++20
#include <iostream>
#include <print> // C++23

int main()
{
    std::cout << std::format("{:b}\n", 0b1010);  // C++20, {:b} formats the argument as binary digits
    std::cout << std::format("{:#b}\n", 0b1010); // C++20, {:#b} formats the argument as 0b-prefixed binary digits

    std::println("{:b} {:#b}", 0b1010, 0b1010);  // C++23, format/print two arguments (same as above) and a newline

    return 0;
}

Wypisuje:

1010
0b1010
1010 0b1010

Czas quizu

Pytanie nr 1

Korzystając z powyższych tabel, ile wynosi 32 w formacie binarnym i szesnastkowym?

Pokaż rozwiązanie

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
193 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze