2012-01-05 50 views
9

Quiero convertir un número binario en un número flotante. He aquí un ejemplo de una posibilidad:Cómo convertir un binario (cadena) en un valor flotante?

>>> float(-0b1110) 

me da el resultado correcto:

Por desgracia, estoy trabajando con binarios cuerdas, es decir, necesito algo así como float('-0b1110').
Sin embargo, esto no funciona:

>>> float('-0b1110') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: invalid literal for float(): -0b1110 

He intentado utilizar binascii.a2b_qp(string[, header]) que convierte un bloque de datos imprimibles citado de nuevo a binario y devuelve los datos binarios. Pero con el tiempo, me sale el mismo error:

>>> float(binascii.a2b_qp('-0b1110')) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: invalid literal for float(): -0b1110 

entiendo los casos en los que el número de salida es un número entero, pero lo que si quiero obtener el número 12.546? ¿A qué llamaría la función para la cadena binaria entonces?

+0

¿Cómo sería la representación binaria de '12.546' parecer - es IEEE 754 binary32 o binary64? Si no, ¿cómo se representa el punto binario o dónde se supone su posición en la cadena? – martineau

+0

La representación binaria del flotante 12.546 es IEEE 754 binary64. – deedee

+2

OK, pero ''0b10110011000000'' es la representación binary64 de IEEE 754 de' -14.0', no '-0b1110'. – martineau

Respuesta

6

Otra opción es hacer

from ast import literal_eval 

float_str = "-0b101010101" 
result = float(literal_eval(float_str)) 

A diferencia del "eval" incorporado, literal_eval se puede ejecutar de forma segura incluso con las entradas del usuario, ya que solo puede analizar literalmente Python, y no ejecutará expresiones, lo que significa que tampoco llamará a las funciones.

6
float(int('-0b1110',0)) 

Eso funciona para mí.

Si tiene una cadena de 64 bits que representa un número de punto flotante en lugar de un número entero, puede hacer una conversión de tres pasos: el primer paso convierte la cadena en un número entero, el segundo lo convierte en un 8- cadena de bytes, y el tercero reinterpreta esos bits como un flotador.

>>> import struct 
>>> s = '0b0100000000101001000101111000110101001111110111110011101101100100' 
>>> q = int(s, 0) 
>>> b8 = struct.pack('Q', q) 
>>> struct.unpack('d', b8)[0] 
12.546 

Por supuesto, puede combinar todos esos pasos en una sola línea.

>>> s2 = '0b1100000000101100000000000000000000000000000000000000000000000000' 
>>> struct.unpack('d', struct.pack('Q', int(s2, 0)))[0] 
-14.0 
+0

de acuerdo con wikipedia 0 10000000 10010010000111111011011 este es el valor de pi redondeado para 24 bits de precisión. >>> float (int ('0b01000000010010010000111111011011', 0)) 1078530011.0 –

+0

huh ???????????? – wim

+0

@wim http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers De lo contrario, cuál es el punto en la conversión a float si en solo se puede obtener la parte entera. –

0

Puede usar eval ('') y luego emitirlo como un flotador si es necesario. Ejemplo:

>> eval('-0b1110') 
-14 
>> float(eval('-0b1110')) 
-14.0 
+0

Bueno, pero vea el ast.literal_eval en mi respuesta: es un truco interesante para tener debajo del cinturón. – jsbueno

+1

sí, eval es malo – wim

0

Usted puede convertir un número binario en forma de cadena a un entero mediante el establecimiento de la base a 2 en el built-in int([x[, base]]) función, sin embargo es necesario deshacerse de la primera 0b. A continuación, puede pasar el resultado en float() para obtener su resultado final:

>>> s = '-0b1110' 
>>> float(int(s.replace('0b', ''), 2)) 
-14.0 

edición: Al parecer deshacerse de la 0b sólo es necesario en Python 2.5 y por debajo, la respuesta de Mark trabaja bien para mí en Python 2.6, pero esto es lo que veo en Python 2.5:

>>> int('-0b1110', 2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: invalid literal for int() with base 2: '-0b1110' 
6

En uno de sus comentarios, indicó que el número binario representa un flotante en formato de 8 bytes de longitud IEEE 754 binary64.Sin embargo, eso no es coherente con el valor -0b1110 que mostró como ejemplo, por lo que lo he ignorado y he utilizado el mío que está en el formato correcto como datos de entrada de ejemplo para probar la respuesta que se muestra a continuación.

Básicamente lo que se hace es primero que la cadena binaria se convierta en un valor entero, luego en una cadena de bytes sin procesar que se pasa a struct.unpack() para la conversión final a un valor de coma flotante. La función bin_to_float() que se muestra a continuación dirige el proceso. Aunque no se ilustra, los argumentos de cadenas de entrada binarias pueden tener el prefijo '0b'.

import struct 

def bin_to_float(b): 
    """ Convert binary string to a float. """ 
    bf = int_to_bytes(int(b, 2), 8) # 8 bytes needed for IEEE 754 binary64 
    return struct.unpack('>d', bf)[0] 

def int_to_bytes(n, minlen=0): # helper function 
    """ Int/long to byte string. """ 
    nbits = n.bit_length() + (1 if n < 0 else 0) # plus one for any sign bit 
    nbytes = (nbits+7) // 8 # number of whole bytes 
    b = bytearray() 
    for _ in range(nbytes): 
     b.append(n & 0xff) 
     n >>= 8 
    if minlen and len(b) < minlen: # zero pad? 
     b.extend([0] * (minlen-len(b))) 
    return bytearray(reversed(b)) # high bytes first 

# tests 

def float_to_bin(f): 
    """ Convert a float into a binary string. """ 
    ba = struct.pack('>d', f) 
    ba = bytearray(ba) # convert string to bytearray - not needed in py3 
    s = ''.join('{:08b}'.format(b) for b in ba) 
    return s[:-1].lstrip('0') + s[0] # strip all leading zeros except for last 

for f in 0.0, 1.0, -14.0, 12.546, 3.141593: 
    binary = float_to_bin(f) 
    print('float_to_bin(%f): %r' % (f, binary)) 
    float = bin_to_float(binary) 
    print('bin_to_float(%r): %f' % (binary, float)) 
    print('') 

Salida:

float_to_bin(0.000000): '0' 
bin_to_float('0'): 0.000000 

float_to_bin(1.000000): '11111111110000000000000000000000000000000000000000000000000000' 
bin_to_float('11111111110000000000000000000000000000000000000000000000000000'): 1.000000 

float_to_bin(-14.000000): '1100000000101100000000000000000000000000000000000000000000000000' 
bin_to_float('1100000000101100000000000000000000000000000000000000000000000000'): -14.000000 

float_to_bin(12.546000): '100000000101001000101111000110101001111110111110011101101100100' 
bin_to_float('100000000101001000101111000110101001111110111110011101101100100'): 12.546000 

float_to_bin(3.141593): '100000000001001001000011111101110000010110000101011110101111111' 
bin_to_float('100000000001001001000011111101110000010110000101011110101111111'): 3.141593 
+1

Esto es genial. Exactamente lo que estaba buscando, ¡gracias! – Savara

0

Esto funciona para mí. probado con Python3.4:

def float_to_bin(num): 
    return bin(struct.unpack('!I', struct.pack('!f', num))[0])[2:].zfill(32) 

def bin_to_float(binary): 
    return struct.unpack('!f',struct.pack('!I', int(binary, 2)))[0] 

float_to_bin(bin_to_float(float_to_bin(123.123))) == float_to_bin(123.123) 
>>> True 
Cuestiones relacionadas