Representación binaria de float en Python (bits no hexadecimales)
¿Cómo puedo obtener una cadena de 0
s y 1
s, según los bits de la representación IEEE 754 de un flotante de 32 bits?
Por ejemplo, dada una entrada 1.00
, el resultado debería ser '00111111100000000000000000000000'
.
Puedes hacerlo con el struct
paquete:
import struct
def binary(num):
return ''.join('{:0>8b}'.format(c) for c in struct.pack('!f', num))
Eso lo empaqueta como un flotante ordenado por bytes de red y luego convierte cada uno de los bytes resultantes en una representación binaria de 8 bits y los concatena:
>>> binary(1)
'00111111100000000000000000000000'
Editar : hubo una solicitud para ampliar la explicación. Ampliaré esto usando variables intermedias para comentar cada paso.
def binary(num):
# Struct can provide us with the float packed into bytes. The '!' ensures that
# it's in network byte order (big-endian) and the 'f' says that it should be
# packed as a float. Alternatively, for double-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's binary representation's with 0's 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)
Y el resultado para algunos ejemplos:
>>> 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'
Aquí hay uno feo...
>>> import struct
>>> bin(struct.unpack('!i',struct.pack('!f',1.0))[0])
'0b111111100000000000000000000000'
Básicamente, solo usé el módulo struct para convertir el float en un int...
Aquí hay uno un poco mejor usando ctypes
:
>>> import ctypes
>>> bin(ctypes.c_uint32.from_buffer(ctypes.c_float(1.0)).value)
'0b111111100000000000000000000000'
Básicamente, construyo un archivo float
y uso la misma ubicación de memoria, pero lo etiqueto como archivo c_uint32
. El c_uint32
valor de es un número entero de Python en el que puede usar la bin
función incorporada.
Nota : al cambiar de tipo también podemos realizar la operación inversa
>>> ctypes.c_float.from_buffer(ctypes.c_uint32(int('0b111111100000000000000000000000', 2))).value
1.0
También para flotantes de 64 bits de doble precisión podemos usar el mismo truco usando ctypes.c_double
& ctypes.c_uint64
en su lugar.