Если вам действительно нужно получить непосредственное представление о внутреннем представлении с плавающей точкой, вы можете использовать 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.
Основные трудности:
- Знак, экспонента и дробь не выровнены по границам байтов, но по краям куска (знак плюспоказатель степени: 12 битов; дробь: 52 бита)
- Число нормализовано так, что оно не имеет начальных нулей (так же, как нормализуется научная запись в десятичной системе счисления, чтобы оно не имело ведущих нулей), и, поскольку все знают, что этотам ведущий 1 не сохраняется.
Я могу порекомендовать статью Википедии на эту тему: в ней много полезных примеров.
Но я подозреваю, что вы на самом деле не хотите получатьво внутреннем представлении поплавка.Вместо этого вы хотите фиксированный -точечный двоичный класс, без надоедливых двоичных показателей, который работает почти так же, как вы делали бы это на бумаге, и где деление на степень 2 действительно отражает сдвигтак много бит вправо.
В зависимости от того, сколько работы вы хотите в него внести, вы можете сделать это, определив класс FixedBinary
в качестве подкласса numbers.Real
, где целая часть внутренне представлена одним int
идробная составляющая на другой int
и знак на третью int
, так что 1,25 будет представлено как (1, int(0.25 * 65536), +1)
(или некоторая другая степень 2).
Здесь также показан самый простой способ получить битовое представление вашей дроби.
[править] Я рекомендую хранить знак отдельно.Вы можете хранить его в целочисленной части, или в дробной части, или в обеих, но у всех есть недостатки.
- Если вы сохраните это в знаке дроби, представление отрицательных целых чисел с дополнением до двух будетзатруднит вас, когда вы захотите замаскировать свои биты.
- Если вы не сохраните это в знаке дроби, не будет возможности представить -0,5.
- Если вы неНе сохраняя его в знаке целочисленной части, невозможно будет представить -1,0.
. Мультипликатор 65536 даст вам 4 десятичных знака точности.Вы можете увеличить его, если хотите больше.Я также рекомендую хранить вашу дробь в самых правых битах и просто игнорировать самые левые биты.Другими словами, будьте довольны тем, что двоичная точка находится в середине int
, не настаивайте на том, чтобы она была слева.Это связано с тем, что при умножении вам понадобится запас слева от двоичной точки.
Реализация собственного числового класса требует значительных усилий.