Двоичное представление float в Python (бит не hex)

Как получить строку как двоичное представление IEEE 754 32-битного плавающего?

Пример

1.00 → '00111111100000000000000000000000'

9 ответов

Вы можете сделать это с помощью пакета struct:

import struct
def binary(num):
 return ''.join(bin(ord(c)).replace('0b', '').rjust(8, '0') for c in struct.pack('!f', num))

Он упаковывает его как сетевое байтовое упорядоченное float и затем преобразует каждый из полученных байтов в 8-битное двоичное представление и объединяет их:

>>> binary(1)
'00111111100000000000000000000000'

Edit: Был запрос на разъяснение. Я буду расширять это, используя промежуточные переменные, чтобы прокомментировать каждый шаг.

def binary(num):
 # Struct can provide us with the float packed into bytes. The '!' ensures that
 # it in network byte order (big-endian) and the 'f' says that it should be
 # packed as a float. Alternatively, for ******-precision, you could use 'd'.
 packed = struct.pack('!f', num)
 print 'Packed: %s' % repr(packed)
 # For each character in the returned string, we'll turn it into its corresponding
 # integer code point
 # 
 # [62, 163, 215, 10] = [ord(c) for c in '>\xa3\xd7\n']
 integers = [ord(c) for c in packed]
 print 'Integers: %s' % integers
 # For each integer, we'll convert it to its binary representation.
 binaries = [bin(i) for i in integers]
 print 'Binaries: %s' % binaries
 # Now strip off the '0b' from each of these
 stripped_binaries = [s.replace('0b', '') for s in binaries]
 print 'Stripped: %s' % stripped_binaries
 # Pad each byte binary representation with 0 to make sure it has all 8 bits:
 #
 # ['00111110', '10100011', '11010111', '00001010']
 padded = [s.rjust(8, '0') for s in stripped_binaries]
 print 'Padded: %s' % padded
 # At this point, we have each of the bytes for the network byte ordered float
 # in an array as binary strings. Now we just concatenate them to get the total
 # representation of the float:
 return ''.join(padded)

И результат для нескольких примеров:

>>> binary(1)
Packed: '?\x80\x00\x00'
Integers: [63, 128, 0, 0]
Binaries: ['0b111111', '0b10000000', '0b0', '0b0']
Stripped: ['111111', '10000000', '0', '0']
Padded: ['00111111', '10000000', '00000000', '00000000']
'00111111100000000000000000000000'
>>> binary(0.32)
Packed: '>\xa3\xd7\n'
Integers: [62, 163, 215, 10]
Binaries: ['0b111110', '0b10100011', '0b11010111', '0b1010']
Stripped: ['111110', '10100011', '11010111', '1010']
Padded: ['00111110', '10100011', '11010111', '00001010']
'00111110101000111101011100001010'


Вот уродливый...

>>> import struct
>>> bin(struct.unpack('!i',struct.pack('!f',1.0))[0])
'0b111111100000000000000000000000'

В принципе, я просто использовал модуль struct для преобразования float в int...

Здесь немного лучше, используя ctypes:

>>> import ctypes
>>> bin(ctypes.******.from_buffer(ctypes.c_float(1.0)).value)
'0b111111100000000000000000000000'

В принципе, я строю float и использую ту же ячейку памяти, но я отмечаю ее как ******. Значение ****** представляет собой целое число python, которое можно использовать встроенную функцию bin.


Нашел другое решение, используя модуль bitstring.

import bitstring
f1 = bitstring.BitArray(float=1.0, length=32)
print f1.read('bin')

Вывод:

00111111100000000000000000000000


Эта проблема более тщательно обрабатывается, разбивая ее на две части.

Первым является преобразование float в int с эквивалентным битовым шаблоном:

def float32_bit_pattern(value):
 return sum(ord(b) << 8*i for i,b in enumerate(struct.pack('f', value)))

Затем преобразуем int в строку:

def int_to_binary(value, bits):
 return bin(value).replace('0b', '').rjust(bits, '0')

Теперь объедините их:

>>> int_to_binary(float32_bit_pattern(1.0), 32)
'00111111100000000000000000000000'


Для полноты вы можете добиться этого с помощью numpy, используя:

f = 1.00
int32bits = np.asarray(f, dtype=np.float32).view(np.int32).item() # item() optional

Затем вы можете распечатать это с помощью дополнения, используя спецификатор формата b

print('{:032b}'.format(int32bits))


После просмотра многих похожих вопросов я написал что-то, что, надеюсь, сделает то, что я хотел.

f = 1.00
negative = False
if f < 0:
 f = f*-1
 negative = True
s = struct.pack('>f', f)
p = struct.unpack('>l', s)[0]
hex_data = hex(p)
scale = 16
num_of_bits = 32
binrep = bin(int(hex_data, scale))[2:].zfill(num_of_bits)
if negative:
 binrep = '1' + binrep[1:]

binrep - результат. Каждая часть будет объяснена.

f = 1.00
negative = False
if f < 0:
 f = f*-1
 negative = True

Преобразует число в положительное, если отрицательное, и устанавливает переменную negative в false. Причина этого заключается в том, что разница между положительными и отрицательными двоичными представлениями находится только в первом бите, и это был более простой способ, чем выяснить, что происходит не так, когда весь процесс обрабатывается с отрицательными числами.

s = struct.pack('>f', f) #'?\x80\x00\x00'
p = struct.unpack('>l', s)[0] #1065353216
hex_data = hex(p) #'0x3f800000'

s является шестнадцатеричным представлением двоичного f. это, однако, не в симпатичной форме, в которой я нуждаюсь. То, где p входит. Это int-представление шестнадцатеричных s. И затем другое преобразование, чтобы получить симпатичный гекс.

scale = 16
num_of_bits = 32
binrep = bin(int(hex_data, scale))[2:].zfill(num_of_bits)
if negative:
 binrep = '1' + binrep[1:]

scale является базой 16 для гексагона. num_of_bits равно 32, поскольку float 32 бит, он используется позже, чтобы заполнить дополнительные места 0, чтобы добраться до 32. Получил код для binrep из этого вопроса. Если число было отрицательным, просто измените первый бит.

Я знаю, что это уродливо, но я не нашел приятного пути, и мне это было нужно быстро. Комментарии приветствуются.


Вы можете использовать .format для самого легкого представления бит на мой взгляд:

мой код будет выглядеть примерно так:

def fto32b(flt):
# is given a 32 bit float value and converts it to a binary string
if isinstance(flt,float):
 # THE FOLLOWING IS AN EXPANDED REPRESENTATION OF THE ONE LINE RETURN
 # packed = struct.pack('!f',flt) <- get the hex representation in (!)Big Endian format of a (f) Float
 # integers = []
 # for c in packed:
 # integers.append(ord(c)) <- change each entry into an int
 # binaries = []
 # for i in integers:
 # binaries.append("{0:08b}".format(i)) <- get the 8bit binary representation of each int (00100101)
 # binarystring = ''.join(binaries) <- join all the bytes together
 # return binarystring
 return ''.join(["{0:08b}".format(i) for i in [ord(c) for c in struct.pack('!f',flt)]])
return None

Вывод:

>>> a = 5.0
'01000000101000000000000000000000'
>>> b = 1.0
'00111111100000000000000000000000'


Несколько из этих ответов не работали так, как написано с Python 3, или не дали правильного представления для отрицательных чисел с плавающей запятой. Я нашел следующее для работы для меня (хотя это дает 64-битное представление, которое мне нужно)

def float_to_binary_string(f):
 def int_to_8bit_binary_string(n):
 stg=bin(n).replace('0b','')
 fillstg = '0'*(8-len(stg))
 return fillstg+stg
 return ''.join( int_to_8bit_binary_string(int(b)) for b in struct.pack('>d',f) )


Это немного больше, чем было задано, но это было то, что мне было нужно, когда я нашел эту запись. Этот код предоставит мантиссе, базу и знак 32-битного поплавка IEEE 754.

import ctypes
def binRep(num):
 binNum = bin(ctypes.******.from_buffer(ctypes.c_float(num)).value)[2:]
 print("bits: " + binNum.rjust(32,"0"))
 mantissa = "1" + binNum[-23:]
 print("sig (bin): " + mantissa.rjust(24))
 mantInt = int(mantissa,2)/2**23
 print("sig (float): " + str(mantInt))
 base = int(binNum[-31:-23],2)-127
 print("base:" + str(base))
 sign = 1-2*("1"==binNum[-32:-31].rjust(1,"0"))
 print("sign:" + str(sign))
 print("recreate:" + str(sign*mantInt*(2**base)))
binRep(-0.75)

выход:

bits: 10111111010000000000000000000000
sig (bin): 110000000000000000000000
sig (float): 1.5
base:-1
sign:-1
recreate:-0.75

licensed under cc by-sa 3.0 with attribution.