How to convert a binary (string) into a float value?
I want to convert a binary number into a float number. Here's an example of a possibility:
>>> float(-0b1110)
gives me the correct output:
-14.0
Unfortunately, I am working with binary strings, i.e., I need something like float('-0b1110')
.
However, this doesn't work:
>>> float('-0b1110')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for float(): -0b1110
I tried to use binascii.a2b_qp(string[, header])
which converts a block of quoted-printable data back to binary and returns the binary data. But eventually, I get the same error:
>>> float(binascii.a2b_qp('-0b1110'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for float(): -0b1110
I understand the cases where the output number is an integer but what if I want to obtain the number 12.546? What would the function call for the binary string look like then?
In one of your comments you indicated that the binary number represents a float in 8 byte long IEEE 754 binary64 format. However that is inconsistent with the -0b1110
value you showed as an example, so I've ignored it and used my own which is in the proper format as example input data for testing the answer shown below.
Essentially what is done is first the binary string is converted into an integer value, then next into a string of raw bytes which is passed to struct.unpack()
for final conversion to a floating point value. The bin_to_float()
function shown below drives the process. Although not illustrated, binary input string arguments can be prefixed with '0b'
.
from codecs import decode
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, length): # Helper function
""" Int/long to byte string.
Python 3.2+ has a built-in int.to_bytes() method that could be used
instead, but the following works in earlier versions including 2.x.
"""
return decode('%%0%dx' % (length << 1) % n, 'hex')[-length:]
def float_to_bin(value): # For testing.
""" Convert float to 64-bit binary string. """
[d] = struct.unpack(">Q", struct.pack(">d", value))
return '{:064b}'.format(d)
if __name__ == '__main__':
for f in 0.0, 1.0, -14.0, 12.546, 3.141593:
print('Test value: %f' % f)
binary = float_to_bin(f)
print(' float_to_bin: %r' % binary)
floating_point = bin_to_float(binary) # Round trip.
print(' bin_to_float: %f\n' % floating_point)
Output:
Test value: 0.000000
float_to_bin: '0000000000000000000000000000000000000000000000000000000000000000'
bin_to_float: 0.000000
Test value: 1.000000
float_to_bin: '0011111111110000000000000000000000000000000000000000000000000000'
bin_to_float: 1.000000
Test value: -14.000000
float_to_bin: '1100000000101100000000000000000000000000000000000000000000000000'
bin_to_float: -14.000000
Test value: 12.546000
float_to_bin: '0100000000101001000101111000110101001111110111110011101101100100'
bin_to_float: 12.546000
Test value: 3.141593
float_to_bin: '0100000000001001001000011111101110000010110000101011110101111111'
bin_to_float: 3.141593
This works for me. Tested with 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
Another option is to do
from ast import literal_eval
float_str = "-0b101010101"
result = float(literal_eval(float_str))
Unlike the built-in "eval", literal_eval is safe to be run even on user inputs, as it can only parse Python literals - and will not execute expressions, which means it will not call functions as well.
float(int('-0b1110',0))
That works for me.
If you have a 64-bit string that represents a floating point number rather than an integer, you can do a three-step conversion - the first step turns the string into an integer, the second converts it into an 8-byte string, and the third re-interprets those bits as a float.
>>> import struct
>>> s = '0b0100000000101001000101111000110101001111110111110011101101100100'
>>> q = int(s, 0)
>>> b8 = struct.pack('Q', q)
>>> struct.unpack('d', b8)[0]
12.546
Of course you can combine all those steps into a single line.
>>> s2 = '0b1100000000101100000000000000000000000000000000000000000000000000'
>>> struct.unpack('d', struct.pack('Q', int(s2, 0)))[0]
-14.0
You could use eval('') and then cast it as a float if needed. Example:
>> eval('-0b1110')
-14
>> float(eval('-0b1110'))
-14.0