Выполнение правого сдвига и битовой маскировки на двоичной дроби в python - PullRequest
0 голосов
/ 30 мая 2018

Я ищу способ в python для выполнения сдвига вправо и маскирования битов для двоичного числа, которое также имеет дробную часть.Например, если в числе 1 целое число и 2 дробных бита, то число 0b101 соответствует десятичному значению 1,25.Во-первых, я хочу знать pythonic способ представить это число в python.

Во-вторых, я хочу выполнить 1 правое смещение для этого числа (0b101 >> 1), чтобы результирующее число было 0b010, что будет 0,5 в десятичном виде.Есть ли в питоне внутренний и питонный способ выполнить эту операцию.Точно так же, как замаскировать и получить конкретный бит из двоичного числа?

В настоящее время для сдвига я умножаю число на 2 ** - x, x - количество правых сдвигов.Я не могу думать, что я могу выполнить аналогичную операцию для битовой маски.

1 Ответ

0 голосов
/ 31 мая 2018

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

>>> import struct
>>> a = 1.25
>>> b = struct.pack('>d',a) 
>>> b
b'?\xf4\x00\x00\x00\x00\x00\x00'  # the ? means \x3f, leftmost 7 bits of exponent
>>> a.hex()
'0x1.4000000000000p+0'   

Вы можете замаскировать нужный бит из строки байтов, которая struct.pack()возвращает.

[править] Знак вопроса, представляющий \x3f, объясняется тем, что выходным представлением по умолчанию для строки байтов является строка и Python по возможности покажет asciiсимвол, а не две шестнадцатеричные цифры.

[править] Это представление в принципе зависит от платформы, но на практике это не так, поскольку практически каждый компьютер (даже мэйнфреймы IBM в настоящее время) имеет процессор с плавающей запятой, который используетэтот формат.

Определить, какой бит вам нужен, может быть непросто.

>>> c = struct.pack('>d',a/2)
>>> c
b'?\xe4\x00\x00\x00\x00\x00\x00'
>>> (a/2).hex()
'0x1.4000000000000p-1'

Как видите, деление на 2 - это не совсем одно-битное смещение вправо, котороеКажется, вопрос предполагает, что вы ожидаете.В этом случае деление на 2 уменьшило показатель степени на 1 (с 0x3ff до 0x3fe; с 1023 до 1022) и оставило битовую комбинацию дроби (0x4000) без изменений.Экспонента кажется большой, потому что она смещена на 1023.

Основные трудности:

  1. Знак, экспонента и дробь не выровнены по границам байтов, но по краям куска (знак плюспоказатель степени: 12 битов; дробь: 52 бита)
  2. Число нормализовано так, что оно не имеет начальных нулей (так же, как нормализуется научная запись в десятичной системе счисления, чтобы оно не имело ведущих нулей), и, поскольку все знают, что этотам ведущий 1 не сохраняется.

Я могу порекомендовать статью Википедии на эту тему: в ней много полезных примеров.

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

В зависимости от того, сколько работы вы хотите в него внести, вы можете сделать это, определив класс FixedBinary в качестве подкласса numbers.Real, где целая часть внутренне представлена ​​одним int идробная составляющая на другой int и знак на третью int, так что 1,25 будет представлено как (1, int(0.25 * 65536), +1) (или некоторая другая степень 2).

Здесь также показан самый простой способ получить битовое представление вашей дроби.

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

  1. Если вы сохраните это в знаке дроби, представление отрицательных целых чисел с дополнением до двух будетзатруднит вас, когда вы захотите замаскировать свои биты.
  2. Если вы не сохраните это в знаке дроби, не будет возможности представить -0,5.
  3. Если вы неНе сохраняя его в знаке целочисленной части, невозможно будет представить -1,0.

. Мультипликатор 65536 даст вам 4 десятичных знака точности.Вы можете увеличить его, если хотите больше.Я также рекомендую хранить вашу дробь в самых правых битах и ​​просто игнорировать самые левые биты.Другими словами, будьте довольны тем, что двоичная точка находится в середине int, не настаивайте на том, чтобы она была слева.Это связано с тем, что при умножении вам понадобится запас слева от двоичной точки.

Реализация собственного числового класса требует значительных усилий.

...