Двоичная система счисления — это позиционная система с основанием 2: в ее алфавите две цифры, 0 и 1, а вес каждого разряда равен степени двойки. Число 13 в ней записывается как 1101. Ниже — как читать такую запись, зачем она компьютеру и как работать с двоичными числами в коде: литералы 0b, побитовые операции, маски и сдвиги.
Содержание
Примеры проверены 30.09.2026 на Python 3.14.7 и Node.js 24.21.0.
Три термина, которые часто смешивают:
- двоичная система счисления — способ записать число цифрами 0 и 1;
- бит — один двоичный разряд, который хранит память или передает канал связи;
- двоичный код — любые данные (текст, звук, команды процессора), представленные последовательностью битов по какому-то правилу.
Число 13 остается одним и тем же числом в любой записи: 13, 1101 и D — это три записи одного значения, а не три разных числа.
Алфавит и разряды: как читается двоичное число
Разряды нумеруют справа налево, начиная с нуля. Цифра в разряде с номером k означает, входит ли в число слагаемое 2 в степени k.
| Разряд | 3 | 2 | 1 | 0 |
|---|---|---|---|---|
| Вес разряда | 8 | 4 | 2 | 1 |
| Цифра числа 1101 | 1 | 1 | 0 | 1 |
Складываем веса там, где стоит единица: 8 + 4 + 1 = 13. Левый разряд называют старшим, правый — младшим.
Из этого правила следуют свойства, которые пригодятся в коде:
- k разрядов дают 2 в степени k разных комбинаций: 8 бит — это 256 значений, от 0 до 255;
- младший разряд показывает четность: 0 — число четное, 1 — нечетное;
- если дописать справа ноль, число удвоится: 1101 (13) превращается в 11010 (26);
- число из k единиц равно 2 в степени k минус 1: 1111 — это 15.
Пошаговый перевод между системами и арифметика в столбик разобраны в статье Двоичная система счисления, а обзор остальных систем — в материале Системы счисления и их особенности.
Зачем двоичная система компьютеру
Причин три, и все они инженерные.
- Два состояния проще надежно различить. Элементу схемы достаточно отличать «низкий уровень» от «высокого», а не десять уровней. Запас между уровнями делает схему устойчивой к помехам.
- Арифметика короткая. Таблица сложения для одного разряда состоит из четырех строк, поэтому сумматор собирается из небольшого числа логических элементов.
- Совпадение с логикой. Значения 0 и 1 соответствуют «ложь» и «истина» в булевой алгебре, и одни и те же элементы выполняют и вычисления, и проверку условий.
Это упрощенная картина. Двоичность — выбор инженеров, а не закон природы: в 1958 году в МГУ построили троичную ЭВМ «Сетунь», а ячейки современной флеш-памяти различают больше двух уровней заряда и хранят несколько бит каждая. На уровне логики процессора и программ данные при этом остаются двоичными.
Для человека двоичная запись длинная, поэтому в коде рядом с ней используют шестнадцатеричную: одна шестнадцатеричная цифра заменяет ровно четыре бита, 0b1111_0000 — то же, что 0xF0.
Как записать двоичное число в коде
Минимальный пример на Python. Его можно скопировать и запустить целиком:
n = 0b1101 # двоичный литерал: то же число, что 13
print(n) # 13
print(bin(13)) # 0b1101
print(format(13, "08b")) # 00001101
print(int("1101", 2)) # 13
print(0b1111_0000) # 240
print(0b1101 == 13 == 0xD == 0o15) # True
Что здесь происходит:
0b1101— литерал, то есть число, записанное прямо в тексте программы. Префикс0bсообщает, что дальше идут двоичные цифры. В переменной окажется обычное целое 13;bin()иformat(..., "08b")делают обратное: из числа получают строку. Вариант"08b"дополняет запись нулями до восьми знаков;int("1101", 2)разбирает строку как запись по основанию 2;- подчеркивание разделяет разряды для читаемости и на значение не влияет.
Последняя строка печатает True: двоичная, десятичная, шестнадцатеричная и восьмеричная записи дают одно значение.
Литерал 0b есть в большинстве распространенных языков, отличаются разделитель и функции преобразования:
| Язык | Литерал | Разделитель разрядов | Число в двоичную строку |
|---|---|---|---|
| Python | 0b1101 |
0b1111_0000 |
bin(13), format(13, "b") |
| JavaScript | 0b1101 |
0b1111_0000 |
(13).toString(2) |
| Java | 0b1101 |
0b1111_0000 |
Integer.toBinaryString(13) |
| C++ (с C++14), C (с C23) | 0b1101 |
0b1111'0000 |
C++20: std::format("{:b}", 13); C23: printf("%b", 13u), если формат уже поддерживает библиотека |
Если в строке встретится цифра не из алфавита, разбор завершится ошибкой. Вызов int("102", 2) в Python 3.14 выдает:
ValueError: invalid literal for int() with base 2: '102'
Побитовые операции
Побитовая операция применяет логическое правило к каждому разряду отдельно: И, ИЛИ и исключающее ИЛИ — к парам разрядов двух чисел, инверсия — к разрядам одного числа, сдвиг переносит разряды на заданное число позиций.
| Операция | Запись | Правило для разряда | 1100 и 1010 |
|---|---|---|---|
| И (AND) | a & b |
1, если обе цифры равны 1 | 1000 |
| ИЛИ (OR) | a | b |
1, если хотя бы одна равна 1 | 1110 |
| Исключающее ИЛИ (XOR) | a ^ b |
1, если цифры разные | 0110 |
| НЕ (NOT) | ~a |
меняет каждую цифру на противоположную | зависит от разрядности |
| Сдвиг влево | a << n |
сдвигает цифры на n разрядов влево | 110000 при n = 2 |
| Сдвиг вправо | a >> n |
сдвигает цифры на n разрядов вправо | 0011 при n = 2 |
Проверка в коде:
a, b = 0b1100, 0b1010
results = [
("a & b", a & b),
("a | b", a | b),
("a ^ b", a ^ b),
("a << 2", a << 2),
("a >> 2", a >> 2),
]
for name, value in results:
print(f"{name:6} = {value:06b} = {value}")
print("~a =", ~a)
Вывод:
a & b = 001000 = 8
a | b = 001110 = 14
a ^ b = 000110 = 6
a << 2 = 110000 = 48
a >> 2 = 000011 = 3
~a = -13
Сдвиг влево на n разрядов умножает число на 2 в степени n (12 стало 48), сдвиг вправо делит нацело с отбрасыванием младших разрядов (12 стало 3).
Результат ~a равен -13, а не «0011»: у целых чисел Python нет фиксированной ширины, и инверсия определена как -a - 1. Чтобы получить инверсию в пределах четырех разрядов, результат обрезают маской: ~a & 0b1111 дает 3.
Маски: проверить, установить, сбросить и переключить бит
Маска — это число, в котором единицы стоят в интересующих разрядах. С ее помощью в одном целом числе хранят набор независимых признаков (флагов). Так устроены, например, права доступа к файлам в Unix: чтение — 4, запись — 2, исполнение — 1.
READ, WRITE, EXEC = 0b100, 0b010, 0b001
rights = READ | WRITE # собрать флаги
print(f"{rights:03b}") # 110
print(bool(rights & WRITE)) # True: бит записи установлен
print(bool(rights & EXEC)) # False
rights |= EXEC # установить бит
print(f"{rights:03b}") # 111
rights &= ~WRITE # сбросить бит
print(f"{rights:03b}") # 101
rights ^= READ # переключить бит
print(f"{rights:03b}") # 001
print((0b101101 >> 2) & 0b111) # 3: три бита, начиная со второго
Четыре приема стоит запомнить как алгоритм:
| Задача | Прием |
|---|---|
| Проверить бит | value & mask не равно нулю |
| Установить бит | value | mask |
| Сбросить бит | value & ~mask |
| Переключить бит | value ^ mask |
Маску для разряда с номером k получают сдвигом: 1 << k. Последняя строка примера показывает, как достать поле из нескольких битов: сдвинуть число вправо до нужного разряда и обрезать маской нужной ширины.
Где ошибаются: приоритет, ширина числа и знак
Приоритет операций в JavaScript. Сравнение там выполняется раньше побитового И (так же в C и Java).
const x = 6;
console.log(x & 1 == 0); // 0: сначала 1 == 0, потом 6 & false
console.log((x & 1) == 0); // true: число четное
Первая строка печатает 0, и условие с ней считается ложным, хотя 6 — четное число. Исправление — скобки вокруг побитовой операции. В Python сравнение имеет более низкий приоритет, чем &, но скобки и там делают код однозначным.
Ширина числа. Побитовые операции в JavaScript работают с 32-разрядными знаковыми целыми, а величина сдвига берется по модулю 32:
console.log(1 << 31); // -2147483648: единица попала в знаковый разряд
console.log(1 << 32); // 1: сдвиг на 32 равен сдвигу на 0
console.log(2 ** 32); // 4294967296
console.log(1n << 32n); // 4294967296n: тип BigInt без ограничения ширины
В Java у типа int те же два результата: -2147483648 и 1. В Python целые не ограничены по ширине, и 1 << 100 дает точное число из 31 десятичной цифры.
Символ ^ — не степень. В Python 2 ^ 3 равно 1 (XOR для 10 и 11), а степень записывается как 2 ** 3 и дает 8.
Отрицательные числа. bin(-5) в Python возвращает -0b101 — знак и запись модуля, а не то, что лежит в памяти. Восьмибитное представление в дополнительном коде получают маской: format(-5 & 0xFF, "08b") возвращает 11111011. Сдвиг вправо для отрицательных чисел в Python округляет вниз: -7 >> 1 равно -4, как и -7 // 2.
Если двоичную строку нужно получать для чисел заданной ширины, проверки стоит собрать в функцию:
def to_bits(value, width=8):
if isinstance(value, bool) or not isinstance(value, int):
raise TypeError("нужно целое число")
if isinstance(width, bool) or not isinstance(width, int) or width < 1:
raise ValueError("ширина должна быть целым числом от 1")
if not 0 <= value < 2 ** width:
raise ValueError(f"число не помещается в {width} бит")
return format(value, f"0{width}b")
print(to_bits(13))
print(to_bits(255))
for bad in (256, -1, True, "13", 13.0):
try:
print(to_bits(bad))
except (TypeError, ValueError) as error:
print(repr(bad), "->", type(error).__name__, error)
Вывод:
00001101
11111111
256 -> ValueError число не помещается в 8 бит
-1 -> ValueError число не помещается в 8 бит
True -> TypeError нужно целое число
'13' -> TypeError нужно целое число
13.0 -> TypeError нужно целое число
Отдельная проверка на bool нужна потому, что в Python True является целым числом 1 и без нее функция молча вернула бы 00000001. Ширину проверяем так же: без этого to_bits(0, 0) вернул бы строку 0 из одного знака при заявленной ширине 0, а отрицательная или дробная ширина дала бы непонятную ошибку формата.
Выводы
- Двоичная система счисления — позиционная, с основанием 2: цифры 0 и 1, вес разряда с номером k равен 2 в степени k.
- Компьютеру она удобна по инженерным причинам: два состояния легко различить, арифметика и логика строятся на одних и тех же элементах.
- В коде двоичное число записывают литералом
0b; это обычное целое, меняется только форма записи. - Побитовые операции и маски позволяют проверять, устанавливать, сбрасывать и переключать отдельные биты; маска разряда k — это
1 << k. - Результат сдвигов и инверсии зависит от ширины и знака целого типа в конкретном языке: в Python целые не ограничены, в JavaScript побитовые операции 32-разрядные.
Где применяется / связь с практикой
Двоичная запись и побитовые операции встречаются в сетевых масках и адресах, правах доступа, флагах настроек, форматах файлов и протоколах, хешировании, работе с цветом и с микроконтроллерами. За ними стоит математика: степени и логарифмы, остатки от деления, булева логика и комбинаторика подсчета вариантов.
Освойте тему на практике
Эти темы системно разбираются на курсе Базовая математика для цифровых профессий. Посмотреть формат занятий до записи можно на открытых уроках Otus.
FAQ
Почему байт — это 8 бит?
Это сложившийся стандарт, а не свойство двоичной системы: в ранних машинах встречались байты другой длины. Восемь бит дают 256 значений и удобно делятся на две шестнадцатеричные цифры.
Ускоряет ли код замена умножения на сдвиг?
Обычно нет: современные компиляторы и интерпретаторы сами выбирают эффективный вариант. Сдвиг стоит писать там, где по смыслу идет работа с битами, а умножение — там, где считаются величины.
Можно ли записать в двоичном литерале дробное число?
В Python, JavaScript и Java литерал 0b описывает только целые. Дробные числа хранятся в формате с плавающей точкой, у которого свое двоичное устройство.



