Как работать с разными системами счисления в Python
Основные методы перевода между системами счисления в Python
Наиболее эффективный способ перевода чисел между системами счисления в Python – использование встроенных функций int(), bin(), oct(), hex() и форматирования строк. Этот подход покрывает все распространённые случаи (двоичная, восьмеричная, десятичная, шестнадцатеричная системы) и выполняется нативно, без написания дополнительного кода.
Функция int('строка', основание) принимает строку, представляющую число в системе с основанием от 2 до 36, и возвращает десятичное целое. Например:
int('1010', 2) # 10
int('12', 8) # 10
int('A', 16) # 10Python восьмеричная система (восьмеричная система счисления в python)
Для обратного преобразования используются bin(), oct(), hex(), которые возвращают строку с префиксом 0b, 0o или 0x соответственно:
bin(10) # '0b1010'
oct(10) # '0o12'
hex(10) # '0xa'перевод в другие системы счисления python (перевод в другую систему счисления в python)
Чтобы получить строку без префикса, применяют format() или f-строки:
format(10, 'b') # '1010'
format(10, 'o') # '12'
format(10, 'x') # 'a'
f'{10:b}' # '1010'
f'{10:o}' # '12'
f'{10:x}' # 'a'16 система счисления python (перевод в шестнадцатеричную систему в python)
Типичные ошибки и их решения:
- ValueError – если строка содержит недопустимые для данного основания символы. Например,
int('12', 2)вызовет ошибку, так как '2' не является двоичной цифрой. Решение: предварительно проверять строку регулярным выражением или обрабатывать исключение. - Неверный регистр –
int('a', 16)корректно,int('A', 16)тоже, ноint('A', 10)вызовет ошибку. Рекомендуется использовать единый регистр (строчные) после приведенияstr.lower(). - Основание вне диапазона 2–36 –
int('10', 1)илиint('10', 37)приводят кValueError. Для нестандартных оснований (больше 36) нужны собственные алгоритмы.
Когда использовать: если требуется быстрый перевод между системами с основанием 2, 8, 10, 16 или любым другим от 2 до 36, и не нужна особая гибкость (например, собственный алфавит).
Как преобразовать десятичное число в систему с основанием больше 16 или с нестандартным алфавитом?
Встроенные функции ограничены основанием 36. Для произвольного основания (например, 62) необходимо реализовать собственный алгоритм. Классический метод – деление числа на основание с сохранением остатков, которые затем заменяются на символы из заданного алфавита.
def to_base(n, base, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
if n == 0:
return alphabet[0]
result = []
while n > 0:
result.append(alphabet[n % base])
n //= base
return ''.join(reversed(result))
# Примеры
to_base(255, 16) # 'FF'
to_base(255, 8) # '377'
to_base(255, 2) # '11111111'
to_base(1234567890, 62) # '1LY7VK'Python перевод в двоичную систему (перевод числа в двоичную систему в python)
Пояснение: функция повторно делит число на основание, берёт остаток от деления и преобразует его в символ по индексу из алфавита. Полученные символы собираются в обратном порядке, так как первый остаток соответствует младшему разряду.
Проблемы и решения:
- Если алфавит короче основания, возникнет ошибка
IndexError. Решение: заранее проверять длину алфавита (len(alphabet) >= base). - Отрицательные числа – функция не обрабатывает знак. Решение: добавить проверку
if n < 0: return '-' + to_base(-n, base, alphabet). - Дробные числа – требуется отдельный алгоритм для дробной части (умножение на основание).
Когда использовать: для перевода в системы с основанием свыше 36, для кастомного алфавита (например, base64), для учебных целей или при необходимости точного контроля над процессом.
Как получить десятичное число из строки, если основание больше 36 или алфавит нестандартный?
Обратное преобразование – из произвольной системы в десятичную – выполняется путём обхода строки и накопления результата по формуле Горнера или суммированием произведений.
def from_base(s, base, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
char_to_val = {c: i for i, c in enumerate(alphabet)}
result = 0
for char in s.upper():
result = result * base + char_to_val[char]
return result
# Примеры
from_base('FF', 16) # 255
from_base('11111111', 2) # 255
from_base('1LY7VK', 62) # 1234567890перевод в десятичную систему счисления python (перевод числа в десятичную систему счисления в python)
Пояснение: создаётся словарь соответствия символов их числовым значениям. Для каждого символа строки значение добавляется к результату, предварительно умноженному на основание. Метод Горнера позволяет обойтись без возведения в степень, что эффективнее.
Типичные ошибки:
- Символ отсутствует в алфавите –
KeyError. Решение: предварительно проверятьif char not in char_to_val: raise ValueError(...). - Разный регистр – функция
.upper()приводит строку к верхнему регистру, что предполагает алфавит только из заглавных букв. Если алфавит содержит строчные, нужно заменить на.lower()или не изменять регистр. - Основание слишком большое – при большом основании и длинной строке результат может превысить возможности целых чисел Python (но Python поддерживает произвольно большие целые, поэтому это не проблема).
Когда использовать: для разбора строк, полученных из внешних источников, когда стандартное int() не подходит из-за нестандартного алфавита или основания больше 36.
Как перевести дробное десятичное число в другую систему счисления (двоичную, восьмеричную и т.д.)?
Перевод дробной части отличается от целой: дробь многократно умножается на основание, целая часть результата становится очередной цифрой, а дробная часть снова умножается. Процесс продолжается до достижения нужной точности или обнуления дробной части.
def frac_to_base(frac, base, precision=10):
result = []
for _ in range(precision):
frac *= base
digit = int(frac)
result.append(str(digit))
frac -= digit
if frac == 0:
break
return '.' + ''.join(result)
# Пример: 0.625 в двоичной
frac_to_base(0.625, 2) # '.101'
# 0.1 в двоичной (бесконечная дробь)
frac_to_base(0.1, 2, precision=8) # '.00011001'перевод в троичную систему python (перевод числа в троичную систему в python)
Пояснение: функция принимает дробную часть (число от 0 до 1), основание и желаемую точность (количество знаков после запятой). На каждой итерации дробь умножается на основание, целая часть записывается, а от числа остаётся только дробная часть. Процесс останавливается, если дробь стала нулевой, или после достижения заданной точности.
Проблемы:
- Некоторые дроби не представимы точно в системах с основанием, отличным от их собственного (например, 0.1 в двоичной – периодическая дробь). Решение: ограничить точность или использовать символьные вычисления (библиотека
fractions). - Ошибки округления из-за представления чисел с плавающей точкой в Python. Решение: использовать
Decimalиз модуляdecimalдля точного представления.
Когда использовать: при работе с дробными числами, например, в цифровой обработке сигналов, криптографии или визуализации данных, где требуется точное представление дробной части в другой системе.
Как использовать f-строки и format() для вывода в нескольких системах счисления одновременно?
Для вывода числа сразу в нескольких системах удобно использовать f-строки с разными спецификаторами. Это часто применяется при отладке или логировании.
n = 42
print(f'Десятичное: {n}')
print(f'Двоичное: {n:b}')
print(f'Восьмеричное: {n:o}')
print(f'Шестнадцатеричное: {n:x}')
# Результат:
# Десятичное: 42
# Двоичное: 101010
# Восьмеричное: 52
# Шестнадцатеричное: 2a
Пояснение: спецификаторы :b, :o, :x работают только для целых чисел и не поддерживают произвольные основания. Для вывода с префиксом используют :#b, :#o, :#x (например, f'{42:#b}' – '0b101010').
Ошибки:
- Попытка использовать
:bдля дробного числа –ValueError. Спецификаторы бинарного/восьмеричного/шестнадцатеричного формата применимы только к целым числам. - Если нужен вывод с фиксированной шириной поля, можно указать выравнивание, например
f'{n:08b}'– дополнит нулями до 8 бит.
Когда использовать: для быстрого форматирования вывода, в скриптах отладки, при генерации отчётов, где нужно компактно отобразить число в нескольких системах.
Расширенные и нестандартные примеры перевода систем счисления
Прямой перевод из одной произвольной системы в другую (без промежуточного десятичного)
Хотя обычно перевод выполняется через десятичное число, можно реализовать прямой алгоритм, используя операции в исходной системе. Однако на практике проще и быстрее сначала перевести в десятичную, а затем в целевую систему. Ниже представлена универсальная функция convert_base, объединяющая оба шага.
def convert_base(num_str, from_base, to_base,
alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
"""Перевод числа из системы from_base в систему to_base."""
# Шаг 1: from_base -> десятичное
char_to_val = {c: i for i, c in enumerate(alphabet)}
decimal = 0
for char in num_str.upper():
if char not in char_to_val:
raise ValueError(f"Недопустимый символ '{char}' для основания {from_base}")
decimal = decimal * from_base + char_to_val[char]
# Шаг 2: десятичное -> to_base
if decimal == 0:
return alphabet[0]
result = []
while decimal > 0:
result.append(alphabet[decimal % to_base])
decimal //= to_base
return ''.join(reversed(result))
# Примеры
test_cases = [
('1010', 2, 10), # 10
('FF', 16, 2), # 11111111
('123', 4, 16), # 1B (123_4 = 27_10 = 1B_16)
]
for s, fb, tb in test_cases:
print(f"{s} из {fb}-ной в {tb}-ную -> {convert_base(s, fb, tb)}")
1010 из 2-ной в 10-ную -> 10 FF из 16-ной в 2-ную -> 11111111 123 из 4-ной в 16-ную -> 1B
Пояснение: функция последовательно выполняет два преобразования, используя общий алфавит. Обратите внимание, что алфавит должен содержать символы для обеих систем. Если основание превышает длину алфавита, потребуется его расширение.
Проблема: при переводе из системы с основанием больше 36 стандартный алфавит не покрывает все возможные цифры. Решение: задавать собственный алфавит, например, включая строчные буквы или дополнительные символы (как в base64).
Рекурсивный перевод в двоичную систему
Классический рекурсивный алгоритм перевода целого числа в двоичную систему: делить число на 2, рекурсивно обрабатывать частное, а остаток добавлять в конец строки.
def to_binary_recursive(n):
if n == 0:
return '0'
if n == 1:
return '1'
return to_binary_recursive(n // 2) + str(n % 2)
# Примеры
for num in [0, 1, 7, 10, 255]:
print(f"{num} -> {to_binary_recursive(num)}")
0 -> 0 1 -> 1 7 -> 111 10 -> 1010 255 -> 11111111
Пояснение: рекурсия заканчивается, когда число становится 0 или 1. На каждом шаге остаток от деления на 2 (0 или 1) дописывается после рекурсивного вызова, что даёт правильный порядок битов (от старшего к младшему).
Проблема: для очень больших чисел глубина рекурсии может превысить лимит (sys.getrecursionlimit()). Решение: использовать итеративный подход или увеличить лимит, но итеративный способ обычно предпочтительнее.
Перевод с использованием нестандартного алфавита (Base64)
Base64 часто используется в кодировании данных. Его алфавит состоит из 64 символов: A–Z, a–z, 0–9, +, /. Покажем, как перевести число в base64 и обратно, используя собственный алфавит.
BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
def to_base64(n):
return to_base(n, 64, BASE64_ALPHABET)
def from_base64(s):
return from_base(s, 64, BASE64_ALPHABET)
# Пример
num = 12345678901234567890
b64 = to_base64(num)
back = from_base64(b64)
print(f"{num}")
print(f"Base64: {b64}")
print(f"Обратно: {back}")
12345678901234567890 Base64: 9mYSmv0HEu Обратно: 12345678901234567890
Пояснение: функции to_base и from_base из предыдущих разделов принимают произвольный алфавит. Здесь алфавит base64 используется для представления больших чисел компактно. Отметим, что стандартное кодирование base64 в Python (модуль base64) работает с байтами, а не с числами.
Важно: символы '+' и '/' в URL могут быть проблемными. Существует вариант Base64URL (с заменой '+' на '-' и '/' на '_'). При реализации учитывайте контекст использования.
Обработка больших чисел и проверка корректности входных данных
При работе с вводом пользователя необходимо проверять, что строка действительно представляет число в заданной системе. Пример функции с валидацией.
import re
def safe_from_base(s, base, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
"""Безопасное преобразование строки в десятичное число."""
# Убираем лишние пробелы и приводим к верхнему регистру
s = s.strip().upper()
# Строим регулярное выражение для проверки
valid_chars = alphabet[:base]
pattern = f'^[{valid_chars}]+$'
if not re.match(pattern, s):
raise ValueError(f"Строка '{s}' содержит недопустимые символы для основания {base}")
return from_base(s, base, alphabet)
# Тесты
try:
print(safe_from_base('1A', 16)) # 26
print(safe_from_base('1G', 16)) # ошибка
except ValueError as e:
print(f"Ошибка: {e}")
26 Ошибка: Строка '1G' содержит недопустимые символы для основания 16
Пояснение: функция сначала проверяет строку регулярным выражением, которое допускает только символы, входящие в первые base символов алфавита. Затем вызывает уже знакомую from_base.
Проблема: для основания >36 регулярное выражение может стать громоздким, если алфавит содержит спецсимволы. В таких случаях проще проверять каждый символ по словарю, как в from_base.
Работа с отрицательными числами
При переводе отрицательных чисел в другую систему счисления необходимо сохранить знак. Ниже приведён пример, который добавляет знак минус перед результатом.
def signed_to_base(n, base, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
if n < 0:
return '-' + to_base(-n, base, alphabet)
return to_base(n, base, alphabet)
# Примеры
print(signed_to_base(-255, 16)) # -FF
print(signed_to_base(-42, 2)) # -101010
-FF -101010
Пояснение: функция проверяет знак числа. Для отрицательных чисел рекурсивно вызывает себя для модуля числа и добавляет префикс '-'. Аналогично для обратного преобразования можно предусмотреть распознавание знака в строке.
Примечание: в некоторых контекстах (например, в компьютерных науках) отрицательные числа представляются в дополнительном коде. Данный подход не реализует такое представление – он просто сохраняет знак.