Двоичная система счисления — это позиционная система с основанием 2, в которой любое число записывается всего двумя цифрами: 0 и 1. Каждая такая цифра называется битом (binary digit). Разряды считаются справа налево, и вес каждого разряда — это степень двойки: 1, 2, 4, 8, 16 и так далее.
Содержание
- Почему в компьютерах именно два состояния
- Разряды и веса: как читать двоичное число
- Из двоичной в десятичную (по шагам)
- Из десятичной в двоичную (по шагам)
- Биты, байты и группировка разрядов
- Двоичная арифметика: сложение
- Двоичная арифметика: умножение
- Отрицательные числа: дополнительный код
- Тот же расчет на C++
- Частая ошибка новичка
- Выводы
- Где применяется / связь с практикой
- FAQ
Ниже я разберу по шагам четыре вещи, которые обычно и ищут по этому запросу: перевод из двоичной в десятичную и обратно (руками, до формул), двоичную арифметику (сложение, вычитание, умножение), представление отрицательных чисел через дополнительный код и связь битов с байтами. Все переводы сначала показываю на бумаге, потом дублирую кодом с проговоренным результатом.
Почему в компьютерах именно два состояния
Разряд в памяти или на линии связи проще всего сделать двухуровневым: есть сигнал или нет, заряд выше порога или ниже. Два четко различимых уровня устойчивы к помехам, поэтому вся цифровая техника оперирует нулями и единицами. Это инженерное упрощение: физически там не «лампочка горит», а напряжение в диапазоне, которое схема трактует как 0 или 1.
Из этого не следует, что двоичная система «единственно правильная». Для человека она длинная и плохо читается: десятичное 100 — это уже 1100100. Поэтому рядом живут восьмеричная и шестнадцатеричная записи — они короче и переводятся в двоичную по группам разрядов.
Разряды и веса: как читать двоичное число
Самый правый разряд имеет номер 0, следующий влево — 1, затем 2 и так далее. Вес разряда номер k равен 2 в степени k.
| Разряд k | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Вес 2^k | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Держите эту таблицу перед глазами — на ней строятся оба перевода.
Из двоичной в десятичную (по шагам)
Возьмем число 1011 в двоичной записи. Алгоритм:
- Пронумеруем разряды справа налево: цифры 1, 1, 0, 1 получают номера 0, 1, 2, 3.
- Каждую единицу заменим ее весом 2^k, нули пропустим.
- Сложим полученные веса.
Распишу для 1011:
разряд: 3 2 1 0
цифра: 1 0 1 1
вес: 8 4 2 1
вклад: 8 + 0 + 2 + 1 = 11
Значит, 1011 в двоичной = 11 в десятичной. Прием «пропускаем нули» работает потому, что нулевой разряд дает нулевой вклад: домножать вес на 0 незачем.
Проверю кодом на Python. Встроенная функция int принимает основание вторым аргументом:
b = "1011"
print(int(b, 2)) # 11
В консоли появится 11 — совпадает с расчетом руками.
Из десятичной в двоичную (по шагам)
Обратный перевод — деление на 2 с сбором остатков. Возьмем число 11.
- Делим на 2, записываем частное и остаток.
- Повторяем деление уже с частным, пока частное не станет 0.
- Выписываем остатки снизу вверх (от последнего к первому).
Прогон для 11:
11 / 2 = 5, остаток 1
5 / 2 = 2, остаток 1
2 / 2 = 1, остаток 0
1 / 2 = 0, остаток 1
Читаем остатки снизу вверх: 1011. Получили те же 1011, что и в прямом переводе, — это хорошая самопроверка.
Кодом это делают встроенные функции. bin возвращает строку с префиксом 0b, format дает чистые цифры:
n = 11
print(bin(n)) # 0b1011
print(format(n, "b")) # 1011
Вывод: сначала строка 0b1011, затем 1011.
Биты, байты и группировка разрядов
Бит — один двоичный разряд. Байт — группа из 8 бит; это минимальная адресуемая ячейка памяти в большинстве архитектур. В 8 битах помещается 2^8 = 256 разных комбинаций: без знака это числа от 0 до 255.
Отсюда удобный перевод в шестнадцатеричную запись: 4 бита — это ровно одна шестнадцатеричная цифра (0-F), а байт — две такие цифры. Поэтому программисты часто пишут байты в hex: так короче и нагляднее.
| Двоичное (4 бита) | Десятичное | Hex |
|---|---|---|
| 0000 | 0 | 0 |
| 0101 | 5 | 5 |
| 1010 | 10 | A |
| 1111 | 15 | F |
Чтобы перевести байт 11111011 в hex, разбиваем на две тетрады: 1111 и 1011. Это 15 и 11, то есть F и B. Итог: 0xFB.
Двоичная арифметика: сложение
Складывают двоичные числа столбиком, справа налево, ровно как десятичные, только перенос возникает уже при сумме 2. Правила разряда: 0+0=0, 0+1=1, 1+1=10 (пишем 0, переносим 1), 1+1+1=11 (пишем 1, переносим 1).
Сложим 1011 (11) и 0110 (6):
1011
+ 0110
------
10001
Разберу переносы справа налево: 1+0=1; 1+1=10 (0, перенос 1); 0+1+1=10 (0, перенос 1); 1+0+1=10 (0, перенос 1); наверх уходит 1. Результат 10001 = 16 + 1 = 17, и 11 + 6 действительно 17.
Двоичная арифметика: умножение
Умножение проще десятичного: частичное произведение — либо копия множимого (когда цифра множителя 1), либо нули (когда 0), со сдвигом по разряду. Умножим 101 (5) на 11 (3):
101
x 11
------
101 (умножили на младшую 1)
+ 1010 (умножили на старшую 1, сдвиг на разряд)
------
1111
1111 = 8 + 4 + 2 + 1 = 15, и 5 умножить на 3 — это 15.
Отрицательные числа: дополнительный код
Знак минуса в памяти отдельно не хранится — его кодируют самим набором бит. Стандартный способ — дополнительный код (two’s complement). Идея: старший бит фиксированной разрядности несет отрицательный вес. Для 8 бит веса разрядов такие:
| Разряд | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Вес | -128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Чтобы получить дополнительный код отрицательного числа, есть механический рецепт из трех шагов. Закодируем -5 в 8 битах:
- Записываем модуль в двоичном виде: 5 = 00000101.
- Инвертируем все биты (0 меняем на 1 и наоборот): 11111010.
- Прибавляем 1: 11111011.
Проверим через веса старшего разряда: 11111011 = -128 + 64 + 32 + 16 + 8 + 2 + 1 = -5. Сходится.
Почему так удобно: вычитание сводится к сложению с дополнительным кодом, и отдельная схема вычитания процессору не нужна. Посчитаем 11 — 5 как 11 + (-5) в 8 битах:
00001011 (11)
+ 11111011 (-5)
----------
1 00000110 (перенос из старшего разряда отбрасывается)
Оставшиеся 8 бит 00000110 = 6, и 11 — 5 равно 6. Перенос за пределы разрядности в дополнительном коде просто игнорируется — это часть механики, а не ошибка.
Важная граница: диапазон зависит от числа бит. Знаковый байт (8 бит) покрывает не -255..255, а от -128 до 127. Выход за этот предел дает переполнение, и результат «заворачивается». Поэтому разрядность всегда указывают явно.
Проверю дополнительный код кодом. В Python целые не ограничены 8 битами, поэтому маску 8 бит задаю через & 0xFF:
x = -5
print(format(x & 0xFF, "08b")) # 11111011
Вывод 11111011 — тот самый набор бит, что я получил руками.
Тот же расчет на C++
В C++ работа с битами ближе к «железу»: std::bitset печатает разряды явно. Минимальный полный пример:
#include <iostream>
#include <bitset>
int main() {
int n = 11;
std::bitset<8> b(n);
std::cout << b << "\n"; // 00001011
std::cout << b.to_ulong() << "\n"; // 11
signed char neg = -5;
std::bitset<8> nb(static_cast<unsigned char>(neg));
std::cout << nb << "\n"; // 11111011
return 0;
}
Ожидаемый вывод в консоли построчно: 00001011, затем 11, затем 11111011. Первые две строки — прямой и обратный перевод числа 11, третья — дополнительный код -5, который совпал с расчетом на бумаге и с примером на Python.
Частая ошибка новичка
Типичный промах — при переводе десятичного в двоичное выписать остатки в том порядке, в каком они появились, сверху вниз. Для 11 это дало бы 1101 вместо 1011:
n, digits = 11, ""
while n > 0:
digits += str(n % 2) # дописываем остаток в конец
n //= 2
print(digits) # 1101 - неверный порядок
Вывод 1101 читается как 8 + 4 + 1 = 13, а не 11. Остатки нужно переворачивать — собирать в обратном порядке:
n, digits = 11, ""
while n > 0:
digits = str(n % 2) + digits # дописываем остаток в начало
n //= 2
print(digits) # 1011 - верно
Теперь вывод 1011 = 11, ошибка устранена.
Выводы
- Двоичная система позиционная, основание 2, вес разряда k равен 2^k; перевод в десятичную — сумма весов единичных разрядов.
- Обратный перевод — деление на 2 с остатками, которые выписывают снизу вверх; порядок остатков легко перепутать.
- Сложение, вычитание и умножение делаются столбиком, перенос возникает при сумме 2.
- Отрицательные числа кодируют дополнительным кодом (инверсия + 1); вычитание тогда сводится к сложению, а диапазон зависит от разрядности (8 бит со знаком: -128..127).
- Бит — один разряд, байт — 8 бит (256 комбинаций), тетрада бит удобно ложится на одну шестнадцатеричную цифру.
Где применяется / связь с практикой
Понимание битов и дополнительного кода нужно везде, где важна работа с памятью: битовые флаги и маски, сетевые протоколы, форматы файлов, целочисленное переполнение. В C и C++ это база: типы фиксированного размера, знаковые и беззнаковые целые, сдвиги и маски &, |, ^, <<, >> опираются ровно на разобранную здесь механику разрядов.
Освойте тему на практике
Если хочется дойти от «читаю биты руками» до уверенной работы с ними в коде, разобрать это системно помогает курс C++ Basic. Посмотреть формат и уровень занятий можно на открытых уроках Otus — там разбирают конкретные задачи вживую.
FAQ
Чем двоичная система отличается от двоичного кода?
Двоичная система — это способ записи чисел с основанием 2. Двоичный код — более широкий термин: любое представление данных нулями и единицами, включая текст, команды процессора и картинки, а не только числа.
Сколько разных значений помещается в n бит?
Ровно 2 в степени n. В 1 бите — 2 значения, в 4 битах — 16, в 8 битах (байте) — 256. Для знаковых чисел то же количество комбинаций делится между отрицательными и неотрицательными.
Зачем нужны восьмеричная и шестнадцатеричная записи, если техника работает в двоичной?
Это компактная запись тех же битов для человека: 3 бита — одна восьмеричная цифра, 4 бита — одна шестнадцатеричная. Читать 0xFB проще, чем 11111011, а перевод между ними механический.



