Россия и Беларусь начали и продолжают войну против народа Украины. #Буча #Мариуполь #Краматорск #Кременчуг

Урок №44. Конвертация чисел из двоичной системы в десятичную и наоборот

  Юрий  | 

  Обновл. 11 Сен 2021  | 

 199260

 ǀ   79 

Чтобы научиться конвертировать числа из двоичной (бинарной) системы счисления в десятичную и наоборот, прежде всего необходимо понять, как целые числа представлены в двоичной системе. Мы уже немного говорили об этом на уроке №31.

Представление чисел в двоичной системе

Рассмотрим обычное десятичное число, например, число 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. Детально об этом читайте в материалах урока №34.

Результат:

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. Добавление единицы позволяет избежать наличия двух представлений нуля и упрощает внутреннюю логику, необходимую для выполнения арифметических вычислений с отрицательными числами.

Перед тем, как конвертировать двоичное число (используя метод «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?

Еще на уроке №28 мы говорили: «Когда вы указываете тип данных переменной, компилятор и процессор заботятся о деталях конвертации этого значения в соответствующую последовательность бит определенного типа данных. Когда вы просите ваше значение обратно, то оно «восстанавливается» из соответствующей последовательности бит в памяти».

Тип переменной используется для конвертации бинарного представления числа обратно в ожидаемую форму. Поэтому, если вы указали целочисленный тип данных 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 цифры). Не используйте побитовые операторы.

Подсказки:

   Воспользуйтесь способом конвертации №2. Предполагается, что наименьшим числом для сравнения является 128.

   Напишите функцию для проверки входных чисел: являются ли они больше чисел, умноженных на 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.

Ответ: 0101 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 (344 оценок, среднее: 4,88 из 5)
Загрузка...

Комментариев: 79

  1. Николай:

    Задача состоит не в использовании синтаксического сахара, а поиск наиболее оптимального решения на основании пройденного материала и рекомендаций автора.

    Данный фрагмент кода использует только то, что уже было объяснено в предыдущих уроках:

    1. достаточное объявление и реализация функций в одном файле (многофайловые программы ещё не рассматривались, поэтому вынесение реализации и объявления функции в отдельный заголовочный файл и файл реализации нецелесообразно)
    2. оператор % в данном случае более, чем достаточен вместо поиска остатка от деления напрямую
    3. тип bool при выводе интерпретируется как целое число (true = 1, false = 0). Т.к. у нас по условию стоит диапазон чисел от 0 до 255, то можно безопасно использовать результат деления целых чисел, работать будет точно так же.
    4. функция — это маленькая задача (каюсь, в первый раз я сделал одну функцию, которая выполняла всю работу по выводу двоичного представления десятичного числа от 0 до 255. Потом посмотрел у автора и согласился с его решение разбивки программы на функции)
    6. циклов ещё не было, поэтому должны использоваться только условия и функции.

    P.S. У автора хороший подход к подаче материала.

    Совет студентам: представьте что вы играете в Героев. Вам известно только то, что уже было пройдено. Остальное — Terra Incognita. Задания следует выполнять тем инструментарием, который представлен в уроках с 1 по текущий. В этом и заключается обучение — попробовать весь известный на данный момент инструментарий языка программирования.

    1. Сергей:

      Но всё же хорошо знать что же получишь после апгрейда… Побитовые операции будут в следующем уроке, но на сколько сократится код при их применении!

      1. FOX:

        использования sizeof(x) не целесообразно, вить так мы узнаём размер переменной (то есть 4байта, или у тебя просто 4).
        лучше написать 8(как написано в задании)

    2. Aleksandr:

      нужна критика. пысы:учу с++ неделю по этому многого ещё не знаю.

      1. Aleksandr:

        либо же ещё проще

  2. Сергей:

    Мой вариант кода. Есть позаимствования у ребят ниже, но с моими доработками: ввел защиту от некорректного ввода, а также мне не понравилось как сделали ветвление и вывод ответа, поэтому ввел третью контрольную переменную, которая отвечает за "счетчик" вывода битов. Теперь вывод ответа корректен и во всем диапазоне функционирует исправно.

  3. Никита:

    Выполнил задание иначе, т.к решил перебор степеней двойки слишком простым заданием. Использовал рекурсию. Также использовал short int вместо int — какая никакая оптимизация)
    Из минусов отмечу дополнительную ветку ветвления для случая с введённым нулём, т.к. мой способ циклится на нём. Также нарушил правило "единой задачи" для функции, но собственно тут это не страшно.

  4. Genios:

    почему не работает с 4 и 2

  5. Mari:

    Код явно не лучший из представленных, за то целиком и полностью свой. Уже заприметил человека, что сделал похожим образом, но это, вероятно, тот случай, когда одинаково мыслят не гении)))

  6. Максим:

    Думаю если так задачу решать, отдельная функция не особо нужна

  7. Влад:

    1. Дмитрий:

      Сырой код. 0 — не показывает, 1 — не показывает. Для 255 и 256 показывает одинаковый результат.

  8. Сергей:

    Спасибо автору и спасибо всем за игры разума.
    Решил сделать без циклов

    1. Dotamain:

      Какой прекрасный код

    2. Дмитрий:

      Код у Вас, Сергей, красивый и элегантный. Но "глотает" нули, если они в конце.

  9. Fray:

    Мудрено конечно, но работает. Результат выдает правильный, но мне кажется, что я неправильно сделал задание.

  10. Томас:

    Попробовал чуть сократить

  11. ОЛЕГ:

    Всегда с ужасом смотрел на все эти двоичные числа, даже не пытался их понять. Но после этого урока я все понял и даже стал конвертировать числа в уме. Я в диком положительном шоке!!! Огромное спасибо Юрию!!!
    Моя программа:

    main.cpp:

  12. HSerg:

  13. Владислав:

    сделал код на перевод между двумя любыми системами

  14. AHTOH:

    Вот такой вот каменный цветок у меня получился:

  15. DarkSavant:

    У меня так получилось. Проверил — для чисел от 0 до 256 работает нормально. Впрочем поменяв значение переменной d можно конвертировать числа в любом диапазоне.

  16. Artur:

    Буду рад критике

  17. Александр:

  18. Настя:

    Вот мой код. Как мне кажется, самый простой и короткий. Прошу указать на ошибки, если они есть, буду рада критике и коррективам.

  19. Максим:

  20. Pavel:

    Получилось как то так, возможность для масштабирования полная (сразу переделал для чисел, занимающих 2 полных байта):

  21. Руслан:

    Здравствуйте! Спасибо за уроки) Наверное лучшее, что мне удалось найти для первоначального знакомства с С++.
    При написании руководствовался исключительно пройденным материалам до этого урока (хотя ветвление if только вскользь упоминалось, думаю для написания этого задания без него не обойтись).
    Буду очень благодарен конструктивной критике.

    1. Руслан:

      Использую компилятор G++, решил проверить код с дополнительными флагами (-std=c++17 -Wall -Wextra -Werror -Wpedantic -pedantic-errors) и параллельно сократить его. Исправил все появившиеся предупреждения, компиляция и линкинг прошли успешно, программа работает. Однако при пользовательском вводе числа больше 65535 (максимальное значение типа данных unsigned short) возникает ошибка, консоль выводит сообщение "Ошибка сегментирования (стек памяти сброшен на диск)" (это в Linux, в других ОС аналогично).
      Что я делаю не правильно, где может быть ошибка в коде?

      1. Руслан:

        Решил переписав функцию ввода числа

  22. Виктор:

    Интересно для общего ознакомления. На практике лучше использовать калькулятор программиста и не тратить время. Также сведя ошибки к минимуму.

  23. Nikita:

    Сделал так, чтобы пользователь сам выбирал длину числа =)

  24. Майя:

    Я не понимаю, почему в предложенном ответе такой длинный вариант, потому что, мне кажется у меня проще

  25. Сергей:

    Спасибо за задание.
    Циклы не использую. Старался поменьше кода

    1. Dasha:

      Написать программу, которая читает массив из 8 элементов, заполненных 0 и 1 с клавиатуры. Полученный массив считается двоичным числом, которое в свою очередь переводится в десятичный код. Программа считает код слева направо, затем справа налево, получая 2 десятичных кода. При запуске программа подает 2 десятичных числа отсортированных по возрастанию на следующей строке.

  26. Владимир:

    Единственное, я не понял как мне это дело в обратном порядке показать.
    Чтобы читалось слева направо. А не как у меня справа налево.

  27. Константин:

    У меня вот так получилось…

  28. Владимир:

    Подскажите, как выложить здесь код visual studio в оригинале ( черное окно со всей разметкой и т.д.) как народ выкладывает в комментах?
    Есть важный вопрос по операторам, хотелось бы разобраться.

    1. Avatar photo Юрий:

      Просто вставляете код и всё)

  29. AleksTs:

    1. Алексей:

      Бесподобно!!! Самый красивый вариант!