25

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?

martineau
  • 119,623
  • 25
  • 170
  • 301
deedee
  • 311
  • 1
  • 3
  • 8
  • What would the binary representation of `12.546` look like -- is it IEEE 754 binary32 or binary64? If not, how is the binary point represented or where is its assumed position in the string? – martineau Jan 06 '12 at 03:10
  • The binary representation of the float 12.546 is IEEE 754 binary64. – deedee Jan 06 '12 at 14:10
  • 5
    OK, but `'0b10110011000000'` is the IEEE 754 binary64 representation of `-14.0`, not '-0b1110'. – martineau Jan 07 '12 at 03:43
  • 2
    Actually, the IEEE 754 binary64 representation of `-14.0` is `'1100000000101100000000000000000000000000000000000000000000000000'` (in my previous comment the number shown was the little-endian interpretation, but the point made remains the same). – martineau Jan 07 '12 at 18:33
  • `int('-0b1110',2)` returns -14 – peawormsworth Oct 13 '21 at 13:51

7 Answers7

21

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
martineau
  • 119,623
  • 25
  • 170
  • 301
16

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
ucyo
  • 625
  • 7
  • 16
8
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
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • according to wikipedia 0 10000000 10010010000111111011011 this is the value of pi rounded for 24 bits of precision. >>> float(int('0b01000000010010010000111111011011',0)) 1078530011.0 –  Jan 06 '12 at 00:07
  • @wim http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers otherwise what's the point in converting to float if in can only get the integer part. –  Jan 06 '12 at 00:27
  • But Python doesn't support binary floats anyway, so none of these solutions (including `ast.literal_eval`) will work. – kindall Jan 06 '12 at 00:41
  • @nvm, the question uses an integer binary constant as an example. This is completely at odds with the requirement for floating point binary, as the representation of -14.0 is not -0b1110. – Mark Ransom Jan 06 '12 at 01:03
  • @MarkRansom last part of the question, and I quote "I understand the cases where the output number is an integer but what if I want to obtain the number 12.546? How should the function call for the binary string look like then?" –  Jan 06 '12 at 01:07
  • @nvm, that part wasn't in the original question - it was added an hour later. – Mark Ransom Jan 06 '12 at 02:17
8

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.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 1
    I used this code and "0b00111111100000000000000000000000" which should be 1.0 IEEE-754 but I get 1065353216.0? – stephanmg Apr 21 '19 at 16:48
  • My bad, literal_eval wants the little endian representation not big endian? – stephanmg Apr 21 '19 at 16:55
  • Ansewring the 2019 query: a `0b...` literal in Python is a way to create an _integer_ represend in base 2. If you want the bits you type to represent the 32 or 64 bits of a float value, use the `struct` module in the parsed int object to read the same bits as a floating point number. (pack the int to 4/8bytes, and unpack it as the desired float) – jsbueno Dec 05 '22 at 15:52
0

You could use eval('') and then cast it as a float if needed. Example:

>> eval('-0b1110')
-14
>> float(eval('-0b1110'))
-14.0
Fabián Heredia Montiel
  • 1,687
  • 1
  • 16
  • 30
  • Good one, but see the ast.literal_eval on my answer - it is an interesting trick to have under the belt. – jsbueno Jan 06 '12 at 00:06
0

You can convert a binary number in string form to an int by setting the base to 2 in the built-in int([x[, base]]) function, however you need to get rid of the 0b first. You can then pass the result into float() to get your final result:

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

edit: Apparently getting rid of the 0b is only necessary on Python 2.5 and below, Mark's answer works fine for me on Python 2.6 but here is what I see on 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'
Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • You should not strip off the prefix '0b', you should *use it* by passing the base 0. – wim Jul 04 '18 at 18:46
-1
def bin_to_float(binary):
    di = binary.find('.')
    if di == -1:
        return int(binary,2)
    else:
        whole = binary[:di] or '0'
        fraction = binary [di+1:] or '0'
        return int(whole,2) + int(whole,2)/abs(int(whole,2)) * int(fraction,2) / 2** len(fraction)

samples = [
    '-0b1110.010101011',
    '-0b1001110',
    '101',
    '0b1110010.11010111011',
    '1100.',
    '-00011',
    '+0b1100.0011',
]

for binary in samples:
    print(binary,'=',bin_to_float(binary))

Returns

-0b1110.010101011 = -14.333984375
-0b1001110 = -78
101 = 5
0b1110010.11010111011 = 114.84130859375
1100. = 12.0
-00011 = -3
+0b1100.0011 = 12.1875
peawormsworth
  • 1,120
  • 9
  • 9