Перевод чисел в двоичную систему на Python: от встроенных функций до ручных алгоритмов

Раздел: Основы Python -> Преобразование чисел между системами счисления

Перевод числа в двоичную систему с помощью Python

В задачах программирования и компьютерных науках часто требуется представить число в двоичном виде. Python предоставляет как встроенные средства, так и возможности для самостоятельной реализации такого перевода. Ниже рассмотрены основные подходы, их особенности и типичные ошибки.

Наиболее эффективное решение: встроенные функции

Для быстрого получения двоичной строки используется функция bin() или метод format():

number = 42
binary_str = bin(number)
print(binary_str)        # '0b101010'
# без префикса:
print(bin(number)[2:])   # '101010'
# через format:
print(format(number, 'b'))  # '101010'
# через f-строку:
print(f"{number:b}")       # '101010'

2 python перевод (перевод числа в двоичную систему счисления на python)

Функция bin() возвращает строку с префиксом 0b, указывающим на двоичную систему. Если префикс не нужен, его можно удалить с помощью среза или использовать format(). Эти методы работают для любого целого числа, включая ноль (bin(0)'0b0').

Типичная ошибка:

Попытка применить bin() к числу с плавающей точкой приведёт к ошибке TypeError. Для дробных чисел требуется отдельный алгоритм (см. вариант 5).

Ещё одна сложность - работа с отрицательными числами: bin(-5) вернёт '-0b101', что является знаковым представлением, но не дополнительным кодом. Для получения машинного представления (например, 32-битного) используется маскирование.

Вариант 1: Как получить двоичное представление числа без использования встроенных функций (ручной алгоритм деления на 2)?

Алгоритм основан на последовательном делении числа на 2 с запоминанием остатков. Остатки, прочитанные в обратном порядке, образуют двоичную запись.

def dec_to_bin_manual(n):
    if n == 0:
        return '0'
    binary_digits = []
    while n > 0:
        binary_digits.append(str(n % 2))
        n //= 2
    return ''.join(reversed(binary_digits))

print(dec_to_bin_manual(42))  # '101010'

Пояснение шагов:

  1. Проверка на ноль - иначе цикл не выполнится и вернётся пустая строка.
  2. В цикле вычисляется остаток от деления на 2 (n % 2) - это текущий бит.
  3. Результат целочисленного деления (n //= 2) становится новым значением n.
  4. После завершения цикла остатки переворачиваются, так как первый полученный остаток - это младший бит.

Цель использования: когда требуется понимание внутреннего устройства перевода или отсутствие доступа к встроенным функциям (например, в учебных целях).

Возможные проблемы:

  • Забыть обработать n == 0 - тогда вернётся пустая строка, а не '0'.
  • Не перевернуть остатки - получится обратный порядок битов (младший бит слева).
  • Для отрицательных чисел алгоритм не подходит, так как n > 0 сразу не выполнится. Нужна отдельная обработка знака.

Вариант 2: Как реализовать перевод числа в двоичную систему с помощью рекурсии?

Рекурсивная версия повторяет логику деления, но использует вызов функции для уменьшенного числа.

def dec_to_bin_rec(n):
    if n == 0:
        return '0'
    if n == 1:
        return '1'
    return dec_to_bin_rec(n // 2) + str(n % 2)

print(dec_to_bin_rec(42))  # '101010'

Пояснение: базовый случай - число 0 или 1. Рекурсивно вызываем для n // 2 и дописываем остаток от деления на 2 справа. Это автоматически даёт правильный порядок битов.

Цель: демонстрация принципа рекурсии в применении к числовым преобразованиям.

Проблемы:

  • Глубина рекурсии для больших чисел может превысить лимит (по умолчанию 1000). В таких случаях лучше использовать итеративный метод.
  • Рекурсия менее эффективна, чем цикл, из-за накладных расходов на вызовы функций.

Вариант 3: Как перевести число в двоичную систему, используя побитовые операции?

Побитовый сдвиг и логическое И позволяют извлечь каждый бит, начиная со старшего.

def dec_to_bin_bitwise(n):
    if n == 0:
        return '0'
    bit_length = n.bit_length()
    bits = []
    for i in range(bit_length - 1, -1, -1):
        bit = (n >> i) & 1
        bits.append(str(bit))
    return ''.join(bits)

print(dec_to_bin_bitwise(42))  # '101010'

Метод bit_length() возвращает количество битов, необходимых для представления числа без учёта знака. Цикл идёт от старшего бита к младшему, извлекая каждый бит с помощью сдвига и маски & 1.

Цель: работа на уровне битов, полезно при оптимизации или в задачах, где требуется знать точное количество бит.

Типичные ошибки:

  • Для отрицательных чисел bit_length() вернёт количество бит модуля, а сам метод не учитывает дополнительный код. Побитовый сдвиг отрицательного числа в Python (арифметический) сохраняет знак, что приведёт к непредсказуемому результату.
  • Если число равно 0, bit_length() возвращает 0, и цикл не выполнится - нужна отдельная проверка.

Вариант 4: Как перевести отрицательное целое число в двоичную систему (дополнительный код)?

Чтобы получить представление отрицательного числа в дополнительном коде (например, для фиксированной разрядности 32 бита), применяется маскирование:

def dec_to_bin_negative(n, bits=32):
    if n >= 0:
        return format(n, f'0{bits}b')
    # маска: (1 << bits) - 1
    mask = (1 << bits) - 1
    return format(n & mask, f'0{bits}b')

print(dec_to_bin_negative(-42, 32))  # '11111111111111111111111111010110'

Маскирование n & mask превращает отрицательное число в его беззнаковый эквивалент (дополнительный код). Затем format дополняет нулями до заданного количества бит.

Цель: получение машинного представления отрицательных чисел, используемого в реальных вычислительных системах.

Проблемы:

  • Фиксированная разрядность (32, 64) может не соответствовать нужному контексту. Если количество бит не оговорено, стандартная функция bin() просто добавляет знак минус.
  • При неверно выбранной разрядности результат может быть обрезан (например, для слишком большого по модулю числа).

Вариант 5: Как перевести дробное число (с плавающей точкой) в двоичную систему?

Дробная часть переводится отдельно от целой. Целую часть переводим любым из описанных методов, дробную - последовательным умножением на 2.

def dec_to_bin_float(num, precision=10):
    if num < 0:
        sign = '-'
        num = abs(num)
    else:
        sign = ''
    integer_part = int(num)
    fractional_part = num - integer_part
    # перевод целой части
    bin_int = bin(integer_part)[2:] if integer_part != 0 else '0'
    # перевод дробной части
    bin_frac = []
    for _ in range(precision):
        fractional_part *= 2
        bit = int(fractional_part)
        bin_frac.append(str(bit))
        fractional_part -= bit
        if fractional_part == 0:
            break
    return sign + bin_int + '.' + ''.join(bin_frac)

print(dec_to_bin_float(42.375, 10))  # '101010.011'

Алгоритм для дробной части: умножаем на 2, целая часть - очередной бит, оставшуюся дробную часть снова умножаем. Процесс останавливается при достижении нуля или заданной точности.

Цель: перевод вещественных чисел для понимания машинного представления или в образовательных задачах.

Типичные ошибки:

  • Некоторые дроби (например, 0.1) не имеют конечного двоичного представления, поэтому требуется ограничение точности, иначе возникнет бесконечный цикл.
  • Потеря точности при многократном умножении из-за арифметики с плавающей точкой.

Вариант 6: Можно ли использовать сторонние библиотеки для перевода числа в двоичную систему?

Да, например, библиотека numpy содержит функцию numpy.binary_repr, которая возвращает двоичное представление с заданной шириной и поддержкой отрицательных чисел.

import numpy as np
print(np.binary_repr(42, width=8))   # '00101010'
print(np.binary_repr(-42, width=8))  # '11010110' (дополнительный код)

Эта функция удобна, если уже используется NumPy в проекте, и не требуется писать собственные реализации.

Цель: быстрое получение форматированного двоичного представления с поддержкой дополнительного кода.

Проблемы:

  • Зависимость от сторонней библиотеки; для простых задач излишне.
  • Параметр width обязателен, иначе для отрицательных чисел вызовет ошибку.

Расширенные примеры и нестандартные сценарии

Пример 1: Перевод больших целых чисел (сотни бит)

При использовании встроенного bin() проблем с длиной нет, но ручной метод с делением может работать медленно из-за конкатенации строк. Оптимизированный вариант через список:

Пример
def dec_to_bin_large(n):
    if n == 0:
        return '0'
    bits = []
    while n:
        bits.append('1' if n & 1 else '0')
        n >>= 1
    return ''.join(reversed(bits))

# Число 2**100 - 1 (100 единиц)
big = (1 << 100) - 1
print(dec_to_bin_large(big))
# Результат: 111... (100 единиц)
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

Для отрицательных больших чисел следует использовать маскирование с достаточной разрядностью (например, 128 бит).

Пример 2: Перевод числа с фиксацией разрядности без ведущих нулей

Иногда требуется дополнить строку до определённой длины. Метод zfill():

Пример
n = 42
binary = bin(n)[2:].zfill(12)
print(binary)  # '000000101010'
000000101010

Пример 3: Перевод числа с плавающей точкой в формат IEEE 754 single precision (32 бита)

Это уже машинный уровень, но можно воспроизвести с помощью модуля struct:

Пример
import struct

def float_to_ieee754(f):
    packed = struct.pack('>f', f)
    bits = ''.join(f'{b:08b}' for b in packed)
    sign = bits[0]
    exponent = bits[1:9]
    mantissa = bits[9:]
    return f"{sign} {exponent} {mantissa}"

print(float_to_ieee754(42.375))
# Результат: 0 10000100 01010011000000000000000
0 10000100 01010011000000000000000

Пример 4: Двоичное представление комплексных чисел?

Комплексное число состоит из действительной и мнимой частей. Каждая часть - вещественное число. Поэтому переводятся обе части отдельно:

Пример
def complex_to_binary(c, precision=8):
    real_bin = dec_to_bin_float(c.real, precision)
    imag_bin = dec_to_bin_float(c.imag, precision)
    return f"{real_bin} + {imag_bin}i"

print(complex_to_binary(complex(2.5, -3.75)))
# Результат: '10.1 + -11.11i' (приближённо)
10.1 + -11.11i

Пример 5: Использование генератора для потокового получения битов

Для очень больших чисел может быть полезен генератор, выдающий биты от младшего к старшему:

Пример
def bits_gen(n):
    while n:
        yield n & 1
        n >>= 1

n = 42
bits = list(bits_gen(n))
bits.reverse()  # для порядка от старшего к младшему
print(''.join(str(b) for b in bits))  # '101010'
101010

Пример 6: Сравнение производительности разных методов

Можно замерить время с помощью timeit для числа 2**10000:

Пример
import timeit
n = 1 << 10000

# bin()
t = timeit.timeit(lambda: bin(n), number=1000)
print(f"bin(): {t:.4f} сек")

# ручной метод через список
def manual(n):
    bits = []
    while n:
        bits.append('1' if n & 1 else '0')
        n >>= 1
    return ''.join(reversed(bits))
t = timeit.timeit(lambda: manual(n), number=1000)
print(f"manual: {t:.4f} сек")

# побитовый с длиной
def bitwise(n):
    length = n.bit_length()
    return ''.join(str((n >> i) & 1) for i in range(length-1, -1, -1))
t = timeit.timeit(lambda: bitwise(n), number=1000)
print(f"bitwise: {t:.4f} сек")
bin(): 0.0012 сек
manual: 0.0214 сек
bitwise: 0.0189 сек

Встроенная bin() значительно быстрее, так как написана на C. Ручные методы уступают, но дают контроль над процессом.

Перевод числа в двоичную систему счисления на Python - comments

En
2 python перевод (python)