Щоб навчитися конвертувати числа з двійкової (бінарної) системи числення в десяткову та навпаки, перш за все необхідно зрозуміти, як цілі числа представлені в двійковій системі. Ми вже трохи говорили про це, коли розглядали цілочисельні типи даних.
- Представлення чисел в двійковій системі числення
- Конвертація чисел з двійкової системи числення в десяткову
- Спосіб №1: Конвертація чисел з десяткової системи числення в двійкову
- Спосіб №2: Конвертація чисел з десяткової системи числення в двійкову
- Ще один приклад
- Додавання двійкових чисел
- Числа signed і метод “two’s complement”
- Чому так важливий тип даних?
- Тест
- Відповіді
Представлення чисел в двійковій системі числення
Розглянемо звичайне десяткове число, наприклад, 5623. Інтуїтивно зрозуміло, що означають всі ці цифри: (5 * 1000) + (6 * 100) + (2 * 10) + (3 * 1). Так як в десятковій системі числення всього 10 цифр, то кожне значення множиться на множник 10 у степені n. Вираз вище можна записати ще так: (5 * 103) + (6 * 102) + (2 * 101) + (3 * 1).
Двійкові числа працюють за аналогічною схемою, за винятком того, що в системі всього 2 числа (0 і 1) і множник не 10, а 2. Так само як коми (або пробіли) використовуються для поліпшення читабельності великих десяткових чисел (наприклад: 1, 427, 435), двійкові числа пишуться групами (в кожній по 4 цифри), наприклад, 1101 0101.
Десяткове значення | Двійкове значення |
0 | 0 |
1 | 1 |
2 | 10 |
3 | 11 |
4 | 100 |
5 | 101 |
6 | 110 |
7 | 111 |
8 | 1000 |
9 | 1001 |
10 | 1010 |
11 | 1011 |
12 | 1100 |
13 | 1101 |
14 | 1110 |
15 | 1111 |
Конвертація чисел з двійкової системи числення в десяткову
У прикладах нижче ми працюємо з цілочисельними значеннями unsigned.
Розглянемо 8-бітне (1-байтове) двійкове число: 0101 1110. Воно означає (0 * 128) + (1 * 64) + (0 * 32) + (1 * 16) + (1 * 8) + (1 * 4 ) + (1 * 2) + (0 * 1)
. Якщо підсумувати, то отримаємо десяткове 64 + 16 + 8 + 4 + 2 = 94
.
Ось той же процес, але в таблиці. Ми множимо кожну двійкову цифру на її значення, яке визначається її місцезнаходженням. Конвертуємо двійкове 0101 1110 в десяткову систему:
Двійковий символ | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 0 |
* Значення символу | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
= Результат (94) | 0 | 64 | 0 | 16 | 8 | 4 | 2 | 0 |
А тепер конвертуємо двійкове 1001 0111 в десяткову систему:
Двійковий символ | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 1 |
* Значення символу | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
= Результат (151) | 128 | 0 | 0 | 16 | 0 | 4 | 2 | 1 |
Отримуємо:
1001 0111 (двійкове) = 151 (десяткове)
Таким способом можна легко конвертувати і 16-бітні, і 32-бітні двійкові числа, просто додаючи стовпчики. Зверніть увагу, найпростіше починати відлік справа наліво, множачи на 2 кожне наступне значення.
Спосіб №1: Конвертація чисел з десяткової системи числення в двійкову
Перший спосіб конвертації чисел з десяткової системи числення в двійкову полягає в безперервному діленні числа на 2 і записуванні остачі. Якщо залишок (“r” від англ. “remainder”) є, то пишемо 1, якщо немає, то пишемо 0. Потім, читаючи залишки знизу вгору, ми отримаємо готове двійкове число.
Наприклад, конвертуємо десяткове число 148 в двійкову систему числення:
148 / 2 = 74 r0
74 / 2 = 37 r0
37 / 2 = 18 r1
18 / 2 = 9 r0
9 / 2 = 4 r1
4 / 2 = 2 r0
2 / 2 = 1 r0
1 / 2 = 0 r1
Записуємо залишки знизу вгору: 1001 0100.
148 (десяткове) = 1001 0100 (двійкове)
Ви можете перевірити цю відповідь шляхом конвертації двійкового числа назад в десяткову систему:
(1 * 128) + (0 * 64) + (0 * 32) + (1 * 16) + (0 * 8) + (1 * 4) + (0 * 2) + (0 * 1) = 148
Спосіб №2: Конвертація чисел з десяткової системи числення в двійкову
Цей спосіб добре підходить для невеликих двійкових чисел. Розглянемо десяткове 148 ще раз. Яке найбільше число, помножене на 2 (з ряду: 1, 2, 4, 8, 16, 32, 64, 128, 256 і т.д.), менше 148? Відповідь: 128.
148 >= 128? Так, тому 128-й біт дорівнює 1. 148 − 128 = 20
20 >= 64? Ні, тому 64-й біт дорівнює 0.
20 >= 32? Ні, тому 32-й біт дорівнює 0.
20 >= 16? Так, тому 16-й біт дорівнює 1. 20 − 16 = 4
4 >= 8? Ні, тому 8-й біт дорівнює 0.
4 >= 4? Так, тому 4-й біт дорівнює 1. 4 − 4 = 0, що означає, що всі інші біти дорівнюють 0.
Примітка: Якщо відповіддю є “Так”, то ми маємо true
, що означає 1. Якщо відповіддю є “Ні”, то ми маємо false
, що означає 0. Детальніше про це в уроці про логічний тип даних bool.
Результат:
148 = (1 * 128) + (0 * 64) + (0 * 32) + (1 * 16) + (0 * 8) + (1 * 4) + (0 * 2) + (0 * 1) = 1001 0100
Те ж, але в таблиці:
Двійковий символ | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
* Значення символу | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
= Результат (148) | 128 | 0 | 0 | 16 | 0 | 4 | 0 | 0 |
Ще один приклад
Конвертуємо десяткове 117 в двійкову систему числення, використовуючи спосіб №1:
117 / 2 = 58 r1
58 / 2 = 29 r0
29 / 2 = 14 r1
14 / 2 = 7 r0
7 / 2 = 3 r1
3 / 2 = 1 r1
1 / 2 = 0 r1
Запишемо число з залишків (знизу вгору):
117 (десяткове) = 111 0101 (двійкове)
А тепер виконаємо ту ж конвертацію, але використовуючи спосіб №2:
Найбільше число, помножене на 2, але яке менше 117 — це 64.
117 >= 64? Так, тому 64-й біт дорівнює 1. 117 − 64 = 53.
53 >= 32? Так, тому 32-й біт дорівнює 1. 53 − 32 = 21.
21 >= 16? Так, тому 16-й біт дорівнює 1. 21 − 16 = 5.
5 >= 8? Ні, тому 8-й біт дорівнює 0.
5 >= 4? Так, тому 4-й біт дорівнює 1. 5 − 4 = 1.
1 >= 2? Ні, тому 2-й біт дорівнює 0.
1 >= 1? Так, тому 1-й біт дорівнює 1.
Результат:
117 (десяткове) = 111 0101 (двійкове)
Додавання двійкових чисел
У деяких випадках (один з них ми розглянемо нижче) вам може знадобитися виконати операцію додавання двох двійкових чисел. Хоча спочатку це може здатися трохи дивним, але ви швидко до цього звикнете.
Розглянемо додавання наступних двох невеликих двійкових чисел:
0110 (6 в десятковій системі) +
0111 (7 в десятковій системі)
По-перше, числа потрібно записати в стовпчик (як показано вище). Потім, справа наліво і зверху вниз ми додаємо кожен стовпець з цифрами, як ніби це десяткові числа. Так як в бінарній системі є тільки два числа: 0 і 1, то всього є 4 можливих результати:
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0, 1 переносимо в наступну колонку
Почнемо з першої колонки (стовпця):
0110 (6 в десятковій системі) +
0111 (7 в десятковій системі)
----
1
0 + 1 = 1. Легко.
Друга колонка:
1
0110 (6 в десятковій системі) +
0111 (7 в десятковій системі)
----
01
1 + 1 = 0, 1 залишається в пам’яті до наступної колонки.
Третя колонка:
11
0110 (6 в десятковій системі) +
0111 (7 в десятковій системі)
----
101
А ось тут вже трохи складніше. Зазвичай 1 + 1 = 0 і залишається одиниця, яку ми переносимо в наступну колонку. Проте, у нас вже є 1 з попереднього стовпчика і нам потрібно додати ще 1. Що робити? А ось що: 1 залишається, а ще 1 ми переносимо далі.
Остання колонка:
11
0110 (6 в десятковій системі) +
0111 (7 в десятковій системі)
----
1101
0 + 0 = 0, але так як є ще 1, то результат: 1101.
13 (десяткове) = 1101 (двійкове)
Ви запитаєте: “А як додати десяткову одиницю до будь-якого іншого двійкового числа (наприклад, до 1011 0011)?”. Точно так же, як ми це робили вище, тільки числом знизу є двійкова одиниця, наприклад:
1 (переносимо в наступну колонку)
1011 0011 (двійкове число)
0000 0001 (1 в двійковій системі)
---------
1011 0100
Числа signed і метод “two’s complement”
У прикладах вище ми працювали тільки з цілими числами unsigned, які можуть бути тільки додатними. Зараз же ми розглянемо те, як працювати з числами signed, які можуть бути як додатними, так і від’ємними.
З цілими числами signed використовується метод “two’s complement”. Він означає, що найлівіший (найголовніший) біт використовується в якості знакового біту. Якщо значенням знакового біту є 0, то число додатне, якщо 1 — число від’ємне.
Додатні числа signed зберігаються так само, як і додатні числа unsigned (з 0 в якості знакового біту). А ось від’ємні числа signed зберігаються у вигляді зворотних додатних чисел +1. Наприклад, виконаємо конвертацію -5 з десяткової системи числення в двійкову, використовуючи метод “two’s complement”:
Спочатку з'ясовуємо двійкове представлення 5: 0000 0101
Потім інвертуємо всі біти (конвертуємо в протилежні): 1111 1010
Після цього додаємо до числа одиницю: 1111 1011
Конвертація -76 з десяткової системи числення в двійкову:
Представлення додатнього числа 76: 0100 1100
Інвертуємо всі біти: 1011 0011
Додаємо до числа одиницю: 1011 0100
Чому ми додаємо одиницю? Розглянемо це на прикладі 0 (нуля). Якщо протилежністю від’ємного числа є його додатна форма, то 0 має два представлення 0000 0000 (додатний нуль) і 1111 1111 (від’ємний нуль). При додаванні одиниці, в 1111 1111 відбудеться переповнення, і значення зміниться на 0000 0000. Додавання одиниці запобігає 0 від наявності двох представлень і спрощує внутрішню логіку, необхідну для виконання арифметичних обчислень з від’ємними числами.
Перед тим, як конвертувати двійкове число (використовуючи метод “two’s complement”) назад в десяткову систему числення, потрібно спочатку подивитися на знаковий біт. Якщо ним є 0, то сміливо використовуйте способи вище для цілих чисел unsigned. Якщо ж знаковим бітом є 1, то тоді потрібно інвертувати всі біти, потім додати одиницю, потім конвертувати в десяткову систему, і вже після цього змінювати знак десяткового числа на від’ємний (бо знаковий біт спочатку був від’ємним).
Наприклад, виконаємо конвертацію двійкового 1001 1110 (використовуючи метод “two’s complement”) в десяткову систему числення:
Маємо: 1001 1110
Інвертуємо біти: 0110 0001
Додаємо одиницю: 0110 0010
Конвертуємо в десяткову систему числення: (0 * 128) + (1 * 64) + (1 * 32) + (0 * 16) + (0 * 8) + (0 * 4) + (1 * 2) + (0 * 1 ) = 64 + 32 + 2 = 98
Так як вихідний знаковий біт був від’ємним, то результатом є -98.
Чому так важливий тип даних?
Розглянемо двійкове число 1011 0100. Що це за число в десятковій системі числення? Ви, напевно, подумаєте, що це 180, і, якби це було стандартне двійкове число unsigned, то ви були б праві. Однак, якщо тут використовується метод “two’s complement”, то результат буде інший, а саме -76. Також значення ще може бути інше, якщо воно закодовано якимось третім способом.
Так як же C++ розуміє в яке число конвертувати 1011 0100: в 180 чи в -76?
Ще в уроці №31 ми говорили: “Коли ви вказуєте тип даних змінної, компілятор і процесор піклуються про деталі кодування цього значення в відповідну послідовність біт певного типу даних. Коли ви просите ваше значення назад, то воно “відновлюється” з відповідної послідовності біт в пам’яті”.
Тип змінної використовується для конвертації двійкового представлення числа назад в очікувану форму. Так що, якщо ви вказали цілочисельний тип даних unsigned, то компілятор знає, що 1011 0100 — це стандартне двійкове число, а його представленням в десятковій системі числення є 180. Якщо ж типом змінної є цілочисельний тип signed, то компілятор знає, що 1011 0100 закодований за допомогою методу “two’s complement” і його представленням в десятковій системі числення є число -76.
Тест
Завдання №1: Конвертуйте двійкове 0100 1101 в десяткову систему числення.
Завдання №2: Конвертуйте десяткове 93 в 8-бітне двійкове число unsigned.
Завдання №3: Конвертуйте десяткове -93 в 8-бітне двійкове число signed (використовуючи метод “two’s complement”).
Завдання №4: Конвертуйте двійкове 1010 0010 в десяткове unsigned.
Завдання №5: Конвертуйте двійкове 1010 0010 в десяткове signed (використовуючи метод “two’s complement”).
Завдання №6: Напишіть програму, яка просить користувача ввести число від 0 до 255. Виведіть його як 8-бітне двійкове число (в парах по 4 цифри). Не використовуйте побітові оператори.
Підказка №1: Скористайтеся способом конвертації №2. Припустимо, що найменшим числом для порівняння є 128.
Підказка №2: Напишіть функцію для перевірки вхідних чисел: чи є вони більше чисел, помножених на 2 (тобто 1, 2, 4, 8, 16, 32, 64 і 128). Якщо це так, то виводиться 1, якщо ні — виводиться 0.
Відповіді
Відповідь №1
Двійковий символ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
* Значення символу | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
= Результат (77) | 0 | 64 | 0 | 0 | 8 | 4 | 0 | 1 |
Відповідь: 77.
Відповідь №2
Використовуючи спосіб №1:
93 / 2 = 46 r1
46 / 2 = 23 r0
23 / 2 = 11 r1
11 / 2 = 5 r1
5 / 2 = 2 r1
2 / 2 = 1 r0
1 / 2 = 0 r1
Остача знизу вгору: 101 1101.
Використовуючи спосіб №2:
Найбільше число, помножене на 2, але яке менше 93 — це 64.
93 >= 64? Так, 64-й біт дорівнює 1. 93 - 64 = 29.
29 >= 32? Ні, 32-й біт дорівнює 0.
29 >= 16? Так, 16-й біт дорівнює 1. 29 - 16 = 13.
13 >= 8? Так, 8-й біт дорівнює 1. 13 - 8 = 5.
5 >= 4? Так, 4-й біт дорівнює 1. 5 - 4 = 1.
1 >= 2? Ні, 2-й біт дорівнює 0.
1 >= 1? Так, 1-й біт дорівнює 1.
Відповідь: 0101 1101.
Відповідь №3
Ми вже знаємо з попереднього прикладу, що 93 — це 0101 1101
Тому інвертуємо біти: 1010 0010
І додаємо одиницю: 1010 0011
Відповідь: 1010 0011.
Відповідь №4
Працюючи справа наліво:
1010 0010 = (0 * 1) + (1 * 2) + (0 * 4) + (0 * 8) + (0 * 16) + (1 * 32) + (0 * 64) + (1 * 128) = 2 + 32 + 128 = 162
Відповідь: 162.
Відповідь №5
Маємо: 1010 0010
Інвертуємо біти: 0101 1101
Додаємо одиницю: 0101 1110
Конвертуємо в десяткову систему числення: 16 + 64 + 8 + 4 + 2 = 94
Так як тут використовується метод “two’s complement”, а знаковий біт є від’ємним, то результатом є -94
Відповідь: -94.
Відповідь №6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include <iostream> // x - це число, яке ми будемо тестувати // pow - це множник 2 (наприклад, 128, 64, 32 і т.д.) int printandDecrementBit(int x, int pow) { // Перевіряємо, чи є значення x більше певного числа, помноженого на 2 і виводимо біт if (x >= pow) std::cout << "1"; else std::cout << "0"; // Якщо x більше числа, помноженого на 2, то віднімаємо його зі значення if (x >= pow) return x - pow; else return x; } int main() { std::cout << "Enter an integer between 0 and 255: "; int x; std::cin >> x; x = printandDecrementBit(x, 128); x = printandDecrementBit(x, 64); x = printandDecrementBit(x, 32); x = printandDecrementBit(x, 16); std::cout << " "; x = printandDecrementBit(x, 8); x = printandDecrementBit(x, 4); x = printandDecrementBit(x, 2); x = printandDecrementBit(x, 1); return 0; } |