28

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?

5
  • 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? Commented Jan 6, 2012 at 3:10
  • The binary representation of the float 12.546 is IEEE 754 binary64. Commented Jan 6, 2012 at 14:10
  • 6
    OK, but '0b10110011000000' is the IEEE 754 binary64 representation of -14.0, not '-0b1110'. Commented Jan 7, 2012 at 3:43
  • 3
    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). Commented Jan 7, 2012 at 18:33
  • int('-0b1110',2) returns -14 Commented Oct 13, 2021 at 13:51

7 Answers 7

24

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
Sign up to request clarification or add additional context in comments.

Comments

17

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

1 Comment

is there equivalent of this using ctypes and NOT pack/unpack?
9
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

6 Comments

according to wikipedia 0 10000000 10010010000111111011011 this is the value of pi rounded for 24 bits of precision. >>> float(int('0b01000000010010010000111111011011',0)) 1078530011.0
@wim en.wikipedia.org/wiki/… otherwise what's the point in converting to float if in can only get the integer part.
But Python doesn't support binary floats anyway, so none of these solutions (including ast.literal_eval) will work.
@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.
@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?"
|
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.

3 Comments

I used this code and "0b00111111100000000000000000000000" which should be 1.0 IEEE-754 but I get 1065353216.0?
My bad, literal_eval wants the little endian representation not big endian?
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)
0

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

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

1 Comment

Good one, but see the ast.literal_eval on my answer - it is an interesting trick to have under the belt.
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'

1 Comment

You should not strip off the prefix '0b', you should use it by passing the base 0.
-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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.