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 numberW 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ętny | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| Ośmkowy | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 |
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ętny | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| Szesnastkowy | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 10 | 11 |
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.
| Szesnastkowy | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
| Binary | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
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 valueSeparatory 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 ittworzy 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?

