Representación binaria de float en Python (bits no hexadecimales)

Resuelto TheMeaningfulEngineer asked hace 11 años • 14 respuestas

¿Cómo puedo obtener una cadena de 0s y 1s, 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'.

TheMeaningfulEngineer avatar May 08 '13 22:05 TheMeaningfulEngineer
Aceptado

Puedes hacerlo con el structpaquete:

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'
Dan Lecocq avatar May 08 '2013 15:05 Dan Lecocq

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 floaty uso la misma ubicación de memoria, pero lo etiqueto como archivo c_uint32. El c_uint32valor de es un número entero de Python en el que puede usar la binfunció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_uint64en su lugar.

mgilson avatar May 08 '2013 15:05 mgilson