Двоичная арифметика и системы счисления в программировании

Теория систем счисления оживает, когда начинаешь применять её в коде. В этой статье посчитаем в двоичной арифметике вручную, а затем посмотрим, какие инструменты для работы с системами счисления встроены в Python и JavaScript.

Двоичное сложение

Правил всего четыре, и три из них очевидны:

ПримерРезультат
0 + 00
0 + 11
1 + 01
1 + 110 (ноль пишем, единицу переносим)

Сложим столбиком 1011₂ (11) и 110₂ (6). Идём справа налево, как в обычном сложении:

Разряд8421
Перенос11
Первое число1011
Второе число0110
Сумма0001

В разряде двоек 1 + 1 = 10: пишем 0, переносим 1. В разряде четвёрок 0 + 1 + перенос = 10: снова пишем 0 и переносим. В итоге сумма выходит за четыре разряда: 10001₂ = 17₁₀. Проверка: 11 + 6 = 17.

Переполнение — не учебная страшилка. Если результат не помещается в отведённое число бит, старший разряд теряется. Знаменитые баги с «отрицательными» счётчиками очков в играх — именно отсюда.

Умножение и сдвиги

Умножение на 2 в двоичной системе — это приписывание нуля справа, точно как умножение на 10 в десятичной: 101₂ × 2 = 1010₂ (5 × 2 = 10). Деление на 2 — отбрасывание последней цифры.

В программировании эти операции называются битовыми сдвигами: x << 1 сдвигает биты влево (умножает на 2), x >> 1 — вправо (делит на 2 с округлением вниз).

Системы счисления в Python

Python понимает литералы с префиксами и умеет переводить числа в обе стороны:

x = 0b1101      # двоичный литерал
y = 0o755       # восьмеричный
z = 0xFF        # шестнадцатеричный
print(x, y, z)  # 13 493 255

# Из числа — в строку с записью в нужной системе
print(bin(13))   # '0b1101'
print(oct(13))   # '0o15'
print(hex(255))  # '0xff'

Обратный перевод — функция int() со вторым аргументом, основанием системы:

print(int('1101', 2))    # 13
print(int('ff', 16))     # 255
print(int('0xff', 16))   # 255 — префикс не мешает
print(int('zz', 36))     # 1295 — основание может быть до 36

# Срезом убираем префикс '0b', если нужна «чистая» запись
print(bin(13)[2:])       # '1101'
# Или форматируем сразу без префикса и с ведущими нулями
print(format(13, '08b')) # '00001101'

Битовые операции выглядят так:

a = 0b1100
b = 0b1010
print(bin(a & b))   # 0b1000 — И: единицы там, где они у обоих
print(bin(a | b))   # 0b1110 — ИЛИ: хотя бы у одного
print(bin(a ^ b))   # 0b110  — исключающее ИЛИ
print(bin(a << 2))  # 0b110000 — сдвиг влево, умножение на 4

Системы счисления в JavaScript

В JavaScript за перевод отвечают parseInt() и метод toString() с аргументом-основанием:

// Из строки в число: parseInt(строка, основание)
console.log(parseInt('1101', 2));  // 13
console.log(parseInt('ff', 16));   // 255
console.log(parseInt('755', 8));   // 493

// Из числа в строку: число.toString(основание)
console.log((13).toString(2));     // '1101'
console.log((255).toString(16));   // 'ff'
console.log((493).toString(8));    // '755'

// Литералы с префиксами тоже работают
console.log(0b1101, 0o755, 0xff);  // 13 493 255

Всегда передавайте основание в parseInt() явно. Вызов parseInt('0x10') вернёт 16, потому что строка с префиксом 0x трактуется как шестнадцатеричная — и это может стать неожиданностью.

Дополнить строку нулями слева помогает padStart():

const byte = (13).toString(2).padStart(8, '0');
console.log(byte);  // '00001101'

Мини-практика: цвет из RGB в hex

Соберём шестнадцатеричный код цвета из трёх каналов — типичная задача фронтендера:

function rgbToHex(r, g, b) {
  const toHex = (n) => n.toString(16).padStart(2, '0');
  return '#' + toHex(r) + toHex(g) + toHex(b);
}
console.log(rgbToHex(255, 102, 0));  // '#ff6600'

А на Python — обратную: разберём hex-цвет на каналы.

def hex_to_rgb(color):
    color = color.lstrip('#')
    return tuple(int(color[i:i + 2], 16) for i in (0, 2, 4))

print(hex_to_rgb('#ff6600'))  # (255, 102, 0)

Что мы узнали

  • Двоичное сложение держится на одном правиле переноса: 1 + 1 = 10.
  • Умножение и деление на 2 — это сдвиги разрядов: << и >>.
  • В Python переводами занимаются bin(), oct(), hex() и int(строка, основание).
  • В JavaScript — parseInt(строка, основание) и число.toString(основание).
  • Оба языка понимают литералы 0b…, 0o… и 0x….
  • Знание систем счисления — это практика: hex-цвета, права доступа, битовые флаги и маски.
Проверьте себя
1. Чему равна сумма 1011₂ + 110₂ в двоичной системе?
A1111₂
B10001₂
C10010₂
D1100₂
2. Что делает операция `x << 2` над числом x в программировании?
AДелит x на 2
BУмножает x на 2
CУмножает x на 4
DДобавляет 2 к x
3. Что вернёт `int('1101', 2)` в Python?
AСтроку '1101'
BЧисло 1101
CЧисло 13
DОшибку ValueError
4. Какой метод JavaScript переводит число 255 в шестнадцатеричную строку?
Ahex(255)
BparseInt(255, 16)
C(255).toString(16)
DNumber.toHex(255)
Поддержать проект