Работа с системами счисления в языке Python
В языке Python встроены мощные средства для работы с числами в различных системах счисления. Преобразование чисел из одной системы в другую требуется при работе с битовыми масками, сетевыми адресами, криптографией и отладкой низкоуровневого кода. В этой статье рассматриваются основные и продвинутые способы перевода чисел между системами счисления, а также типичные ошибки и пути их решения.
Основные методы преобразования
Наиболее эффективное решение
Самый быстрый и простой способ - использовать встроенные функции bin(), oct(), hex() для перевода десятичного числа в двоичную, восьмеричную и шестнадцатеричную системы соответственно, и функцию int() для обратного преобразования.
# Преобразование десятичного числа в двоичную, восьмеричную и шестнадцатеричную строки
n = 255
binary = bin(n) # '0b11111111'
octal = oct(n) # '0o377'
hexadecimal = hex(n) # '0xff'
print(binary, octal, hexadecimal)
Python перевести число в двоичное (перевод числа в двоичную систему в python)
0b11111111 0o377 0xff
системы счисления чисел в python (системы счисления в python)
Обратное преобразование из строки с указанием основания:
# Преобразование строки в десятичное число
dec = int('11111111', 2) # 255
dec2 = int('377', 8) # 255
dec3 = int('ff', 16) # 255
print(dec, dec2, dec3)
255 255 255
Функция int() также может обрабатывать строки с префиксами, если вторым аргументом указать 0:
dec = int('0b11111111', 0) # 255
dec2 = int('0o377', 0) # 255
dec3 = int('0xff', 0) # 255
print(dec, dec2, dec3)
255 255 255
Возможные проблемы и ошибки
- При передаче в
int()строки с недопустимыми символами возникаетValueError. Например,int('1A', 2)- буква A недопустима в двоичной системе. - Префиксы
0b,0o,0xне принимаются, если вторым аргументом задано конкретное основание (например,int('0b101', 10)вызовет ошибку). Используйтеint('0b101', 0)или удалите префикс. - Для очень больших чисел Python автоматически использует длинную арифметику, поэтому переполнение не возникает.
Цели и случаи использования
bin(),oct(),hex()- для отладки, вывода значений в привычном виде, форматирования логов.int(s, base)- для чтения пользовательского ввода или данных из файлов в других системах счисления.
Как получить двоичное представление числа без префикса '0b'?
Для этого применяются функции format() и f-строки:
n = 42
bin_no_prefix = format(n, 'b') # '101010'
bin_fstring = f"{n:b}" # '101010'
print(bin_no_prefix, bin_fstring)
101010 101010
Можно дополнить число ведущими нулями до заданной ширины:
print(format(n, '08b')) # '00101010'
print(f"{n:08b}") # '00101010'
00101010 00101010
Возможные проблемы
- Если в спецификаторе формата указать неверный тип (например, 'b' для десятичного числа), Python вызовет
ValueError. - Для отрицательных чисел результат содержит знак минус перед строкой:
f"{-42:b}"даст'-101010'.
Цели и случаи использования
- Форматирование чисел для вставки в строки без лишних префиксов.
- Создание отчётов, работа с битовыми полями.
Как перевести десятичное число в систему с произвольным основанием (от 2 до 36)?
Стандартные функции Python поддерживают только основания 2, 8, 16. Для других оснований пишется собственная функция:
def to_base(num, base):
digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
sign = ''
if num < 0:
sign = '-'
num = -num
result = ''
while num > 0:
result = digits[num % base] + result
num //= base
return sign + (result or '0')
# Примеры
print(to_base(255, 2)) # '11111111'
print(to_base(255, 16)) # 'FF'
print(to_base(100, 12)) # '84'
print(to_base(0, 10)) # '0'
11111111 FF 84 0
Возможные проблемы и ошибки
- Для основания >36 не хватит стандартных цифр и букв. Можно расширить строку
digitsдополнительными символами. - Функция не обрабатывает дробные числа. Для дробей требуется отдельная реализация.
- При передаче числа 0 возвращается '0' (благодаря
or '0').
Цели и случаи использования
- Преобразование в системы с основанием 3, 4, 5, 12 (дюжинная система), 20 (двадцатеричная), 32, 36.
- Создание компактных представлений чисел (base36, base62).
Как перевести десятичную дробь в двоичную систему?
Дробная часть переводится последовательным умножением на 2 и выделением целой части:
def frac_to_bin(frac, precision=10):
result = '.'
while frac and len(result) - 1 < precision:
frac *= 2
bit = int(frac)
result += str(bit)
frac -= bit
return result
# Пример: 0.375
print(frac_to_bin(0.375)) # '.011'
# 0.1
print(frac_to_bin(0.1)) # '.0001100110' (приближённо)
.011 .0001100110
Возможные проблемы
- Многие десятичные дроби в двоичной системе имеют бесконечное представление (как 0.1). Точность ограничена параметром
precision. - Для целой части и дроби вместе требуется комбинировать с функцией
to_baseдля целой части.
Цели и случаи использования
- Понимание представления чисел с плавающей точкой в компьютере.
- Работа с пользовательскими форматами чисел (например, точное двоичное представление).
Как представить отрицательное число в двоичной системе (дополнительный код)?
Python хранит отрицательные числа в виде бесконечного дополнительного кода, но для фиксированной разрядности нужно применить маску:
def neg_to_bin(num, bits=8):
if num >= 0:
return format(num, f'0{bits}b')
else:
# дополнительный код: 2**bits + num, затем маска
return format((2**bits + num) & (2**bits - 1), f'0{bits}b')
print(neg_to_bin(-5)) # '11111011'
print(neg_to_bin(5)) # '00000101'
11111011 00000101
Возможные проблемы
- Необходимо явно указывать разрядность. Для отрицательного числа с неверной разрядностью результат будет неверным.
- Встроенный
bin(-5)возвращает'-0b101', что не является дополнительным кодом.
Цели и случаи использования
- Низкоуровневое программирование, работа с регистрами, эмуляция работы процессора.
- Отладка битовых операций с отрицательными числами.
Как преобразовать строку, представляющую число в любой системе (от 2 до 36) в десятичное число?
С помощью int() с указанием основания:
dec = int('1A', 16) # 26
dec2 = int('1101', 2) # 13
dec3 = int('77', 8) # 63
# Если строка содержит префикс, используйте base=0
dec4 = int('0x1A', 0) # 26
print(dec, dec2, dec3, dec4)
26 13 63 26
Возможные проблемы
- Строка не должна содержать лишних пробелов или символов перевода строки. Используйте
strip(). - Буквы могут быть как в верхнем, так и в нижнем регистре:
int('ff', 16)иint('FF', 16)работают одинаково. - Если основание меньше 2 или больше 36, вызывается
ValueError.
Цели и случаи использования
- Обработка пользовательского ввода (например, ввод шестнадцатеричного адреса).
- Парсинг файлов конфигурации или протоколов, где числа представлены в разных системах.
Расширенные примеры преобразования систем счисления
Пример 1: Преобразование в base62 (смешанный регистр)
Base62 использует цифры, заглавные и строчные буквы (0-9, A-Z, a-z). Функция расширяет диапазон цифр:
def to_base62(num):
digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
if num == 0:
return '0'
result = ''
while num > 0:
result = digits[num % 62] + result
num //= 62
return result
print(to_base62(1000)) # 'g8'
print(to_base62(123456789)) # '8m0Kx'
g8 8m0Kx
Обратное преобразование из base62:
def from_base62(s):
digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
base = 62
result = 0
for ch in s:
result = result * base + digits.index(ch)
return result
print(from_base62('g8')) # 1000
print(from_base62('8m0Kx')) # 123456789
1000 123456789
Пример 2: Форматирование с выравниванием и знаком
Спецификатор формата позволяет добавлять знак (+) для положительных чисел:
n = 42
print(format(n, '+08b')) # '+0101010' (знак плюс, 7 бит + знак)
print(f"{n:+08b}") # '+0101010'
print(format(-42, '+08b')) # '-0101010'
print(format(-42, '08b')) # '-0101010' (знак минус автоматически)
+0101010 +0101010 -0101010 -0101010
Пример 3: Использование float.hex() для дробных чисел
Встроенный метод float.hex() даёт точное шестнадцатеричное представление числа с плавающей точкой:
num = 12.5
print(num.hex()) # '0x1.9000000000000p+3'
# Это число 1.9 (шестнадцатеричная дробь) умножить на 2^3 = 12.5
0x1.9000000000000p+3
Обратное преобразование: float.fromhex('0x1.9000000000000p+3') возвращает 12.5.
Пример 4: Преобразование больших чисел (2^1000)
Python без труда обрабатывает огромные целые числа. Преобразование в двоичную строку:
big = 2**1000
binary_str = bin(big)
print(binary_str[:50], '...') # первые 50 символов
print('Длина строки:', len(binary_str))
0b100000000000000000000000000000000000000000000000001 ... Длина строки: 1003
Пример 5: Группировка двоичных цифр с разделителями
Для удобства чтения можно вставить пробелы или подчёркивания через каждые 4 бита:
def grouped_bin(n, sep='_'):
s = bin(n)[2:]
# Разбить на группы справа налево
groups = []
while s:
groups.append(s[-4:])
s = s[:-4]
return sep.join(reversed(groups))
print(grouped_bin(255)) # '1111_1111'
print(grouped_bin(65535)) # '1111_1111_1111_1111'
print(grouped_bin(0xABCD)) # '1010_1011_1100_1101'
1111_1111 1111_1111_1111_1111 1010_1011_1100_1101
Пример 6: Сравнение производительности bin() и format()
Для массовых преобразований можно измерить время выполнения:
import timeit
n = 123456789
# bin()
time_bin = timeit.timeit(lambda: bin(n), number=1000000)
# format()
time_format = timeit.timeit(lambda: format(n, 'b'), number=1000000)
print(f"bin: {time_bin:.4f} сек, format: {time_format:.4f} сек")
bin: 0.2345 сек, format: 0.3456 сек (примерные значения)
Обычно bin() немного быстрее, но для единичных операций разница незаметна.