web-dev-qa-db-fra.com

Représentation binaire de float en Python (bits non hexadécimaux)

Comment obtenir la chaîne sous forme de représentation binaire IEEE 754 d'un flottant 32 bits?

Exemple

1,00 -> '001111111000000000000000000000000000'

46

Vous pouvez le faire avec le package struct:

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

Cela le conditionne comme un flottant ordonné par réseau, puis convertit chacun des octets résultants en une représentation binaire 8 bits et les concatène:

>>> binary(1)
'00111111100000000000000000000000'

Modifier : Il y a eu une demande pour développer l'explication. Je développerai cela en utilisant des variables intermédiaires pour commenter chaque étape.

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)

Et le résultat pour quelques exemples:

>>> 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'
53
Dan Lecocq

En voici un laid ...

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

Fondamentalement, je viens d'utiliser le module struct pour convertir le flotteur en int ...


Voici un peu mieux en utilisant ctypes:

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

Fondamentalement, je construis un float et j'utilise le même emplacement mémoire, mais je le marque comme un c_uint. La valeur de c_uint Est un entier python sur lequel vous pouvez utiliser la fonction intégrée bin.

29
mgilson

J'ai trouvé une autre solution en utilisant le module bitstring .

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

Sortie:

00111111100000000000000000000000
20

Ce problème est traité plus proprement en le divisant en deux parties.

La première consiste à convertir le flottant en un entier avec le modèle de bits équivalent:

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

Convertissez ensuite l'int en chaîne:

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

Maintenant, combinez-les:

>>> int_to_binary(float32_bit_pattern(1.0), 32)
'00111111100000000000000000000000'
8
Mark Ransom

Par souci d'exhaustivité, vous pouvez y parvenir avec numpy en utilisant:

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

Vous pouvez ensuite l'imprimer, avec un rembourrage, en utilisant le spécificateur de format b

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

Après avoir parcouru de nombreuses questions similaires, j'ai écrit quelque chose qui, je l'espère, fait ce que je voulais.

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 est le résultat. Chaque partie sera expliquée.


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

Convertit le nombre en positif s'il est négatif et définit la variable négative sur false. La raison en est que la différence entre les représentations binaires positives et négatives est juste dans le premier bit, et c'était la manière la plus simple que de comprendre ce qui ne va pas lorsque l'on fait tout le processus avec des nombres négatifs.


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

s est une représentation hexadécimale du binaire f. il n'est cependant pas dans la jolie forme dont j'ai besoin. C'est là que p entre en jeu. C'est la représentation int de l'hex s. Et puis une autre conversion pour obtenir un joli hex.


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

scale est la base 16 de l'hex. num_of_bits est 32, comme float est 32 bits, il est utilisé plus tard pour remplir les places supplémentaires avec 0 pour arriver à 32. Vous avez le code pour binrep de cette question . Si le nombre était négatif, changez simplement le premier bit.


Je sais que c'est moche, mais je n'ai pas trouvé de bonne façon et j'en avais besoin rapidement. Les commentaires sont les bienvenus.

C'est un peu plus que ce qui était demandé, mais c'était ce dont j'avais besoin quand j'ai trouvé cette entrée. Ce code donnera la mantisse, la base et le signe du flottant 32 bits IEEE 754.

import ctypes
def binRep(num):
    binNum = bin(ctypes.c_uint.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)

sortie:

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

Plusieurs de ces réponses n'ont pas fonctionné comme écrit avec Python 3, ou n'ont pas donné la représentation correcte pour les nombres à virgule flottante négatifs. J'ai trouvé ce qui suit fonctionner pour moi (bien que cela donne 64 bits représentation qui est ce dont j'avais besoin)

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) )
0
dkhammond

Vous pouvez utiliser le .format pour la représentation la plus simple des bits à mon avis:

mon code ressemblerait à quelque chose comme:

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

Sortie:

>>> a = 5.0
'01000000101000000000000000000000'
>>> b = 1.0
'00111111100000000000000000000000'
0
Robert Hughes