You can do that with the struct package:
import struct
def binary(num):
return ''.join('{:0>8b}'.format(c) for c in struct.pack('!f', num))
That packs it as a network byte-ordered float, and then converts each of the resulting bytes into an 8-bit binary representation and concatenates them out:
>>> binary(1)
'00111111100000000000000000000000'
Edit: There was a request to expand the explanation. I'll expand this using intermediate variables to comment each step.
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)
And the result for a few examples:
>>> 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'
Answer from Dan Lecocq on Stack OverflowYou can do that with the struct package:
import struct
def binary(num):
return ''.join('{:0>8b}'.format(c) for c in struct.pack('!f', num))
That packs it as a network byte-ordered float, and then converts each of the resulting bytes into an 8-bit binary representation and concatenates them out:
>>> binary(1)
'00111111100000000000000000000000'
Edit: There was a request to expand the explanation. I'll expand this using intermediate variables to comment each step.
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)
And the result for a few examples:
>>> 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'
Here's an ugly one ...
>>> import struct
>>> bin(struct.unpack('!i',struct.pack('!f',1.0))[0])
'0b111111100000000000000000000000'
Basically, I just used the struct module to convert the float to an int ...
Here's a slightly better one using ctypes:
>>> import ctypes
>>> bin(ctypes.c_uint32.from_buffer(ctypes.c_float(1.0)).value)
'0b111111100000000000000000000000'
Basically, I construct a float and use the same memory location, but I tag it as a c_uint32. The c_uint32's value is a python integer which you can use the builtin bin function on.
Note: by switching types we can do reverse operation as well
>>> ctypes.c_float.from_buffer(ctypes.c_uint32(int('0b111111100000000000000000000000', 2))).value
1.0
also for double-precision 64-bit float we can use the same trick using ctypes.c_double & ctypes.c_uint64 instead.
float to binary conversion
Converting float column values (both whole part and decimal part) into binary in python
python - How to convert a binary (string) into a float value? - Stack Overflow
python - Float to binary - Stack Overflow
Videos
Hi guys, I am trying to find a simple solution for beginners to use for converting a float number into binary, however without using the inbuilt 'bin()' function - if anyone has any pointers it would be much appreciated. I understand how the conversion process works, with the integral part of the number requiring dividing by 2 until no remainder, and with the fractional, you multiply by 2 until no remainder. I am struggling with how to implement this into a python program.
many thanks!!
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
For floats there is built-in method hex().
http://docs.python.org/library/stdtypes.html#float.hex
It gives you the hexadecimal representation of a given number. And translation form hex to binary is trivial.
For example:
In [15]: float.hex(1.25)
Out[15]: '0x1.4000000000000p+0'
In [16]: float.hex(8.25)
Out[16]: '0x1.0800000000000p+3'
Next answer with a bit of theory.
Explanation below does not explain IEEE Floating Point standard only general ideas concerning representation of floating point numbers
Every float number is represented as a fractional part multiplied by an exponent multiplied by a sign. Additionally there is so called bias for exponent, which will be explained bellow.
So we have
- Sign bit
- Fractional part digits
- Exponent part digits
Example for base 2 with 8 bit fraction and 8 bit exponent
Bits in fraction part tell us which summands (numbers to be added) from sequence below are to be included in represented number value
2^-1 + 2^-2 + 2^-3 + 2^-4 + 2^-5 + 2^-6 + 2^-7 + 2^-8
So if you have say 01101101 in fractional part it gives
0*2^-1 + 1*2^-2 + 1*2^-3 + 0*2^-4 + 1*2^-5 + 1*2^-6 + 0*2^-7 + 1*2^-8 = 0.42578125
Now non-zero numbers that are representable that way fall between 2 ** -8 = 0.00390625 and 1 - 2**-8 = 0.99609375
Here the exponent part comes in. Exponent allows us to represent very big numbers by multiplying the fraction part by exponent. So if we have an 8bit exponent we can multiply the resulting fraction by numbers between 0 and 2^255.
So going back to example above let's take exponent of 11000011 = 195.
We have fractional part of 01101101 = 0.42578125 and exponent part 11000011 = 195. It gives us the number 0.42578125 * 2^195, this is really big number.
So far we can represent non-zero numbers between 2^-8 * 2^0 and (1-2^-8) * 2^255. This allows for very big numbers but not for very small numbers. In order to be able to represent small numbers we have to include so called bias in our exponent. It is a number that will be always subtracted from exponent in order to allow for representation of small numbers.
Let's take a bias of 127. Now all exponents are subtracted 127. So numbers that can be represented are between 2^-8 * 2^(0 - 127) and (1-2^-8) * 2^(255 - 127 = 128)
Example number is now 0.42578125 * 2^(195-127 = 68) which is still pretty big.
Example ends
In order to understand this better try to experiment with different bases and sizes for fractional and exponential part. At beginning don't try with odd bases because it only complicates things necessary.
Once you grasp how this representation works you should be able to write code to obtain representation of any number in any base, fractional/exponential part combination.
def float_to_binary(num):
exponent=0
shifted_num=num
while shifted_num != int(shifted_num):
shifted_num*=2
exponent+=1
if exponent==0:
return '{0:0b}'.format(int(shifted_num))
binary='{0:0{1}b}'.format(int(shifted_num),exponent+1)
integer_part=binary[:-exponent]
fractional_part=binary[-exponent:].rstrip('0')
return '{0}.{1}'.format(integer_part,fractional_part)
def floathex_to_binary(floathex):
num = float.fromhex(floathex)
return float_to_binary(num)
print(floathex_to_binary('0x1.a000000000000p+2'))
# 110.1
print(floathex_to_binary('0x1.b5c2000000000p+1'))
# 11.01101011100001
Explanation:
float.fromhex returns a float num. We'd like its binary representation.
{0:b}.format(...) returns binary representations of integers, but not floats.
But if we multiply the float by enough powers of 2, that is, shift the binary representation to the left enough places, we end up with an integer, shifted_num.
Once we have that integer, we are home free, because now we can use {0:b}.format(...).
We can re-insert the decimal point (err, binary point?) by using a bit of string slicing based on the number of places we had shifted to the left (exponent).
Technical point: The number of digits in the binary representation of shifted_num may be smaller than exponent. In that case, we need to pad the binary representation with more 0's on the left, so binary slicing with binary[:-exponent] won't be empty. We manage that with '{0:0{1}b}'.format(...). The 0{1} in the format string sets the width of the formated string to {1}, padded on the left with zeros. (The {1} gets replaced by the number exponent.)
Note that the binary form of 0x1.a000000000000p+2 isn't 101.1 (or more exactly 0b101.1 )
but 0b110.1 (in my Python 2.7, binary numbers are displayed like that)
.
First, a useful method of float instances float.hex() and its inverse function, a float class method float.fromhex()
fh = 12.34.hex()
print fh
print float.fromhex(fh)
result
0x1.8ae147ae147aep+3 # hexadecimal representation of a float
12.34
"Note that float.hex() is an instance method, while float.fromhex() is a class method."
http://docs.python.org/library/stdtypes.html#float.fromhex
.
Secondly, I didn't find a Python's function to transform an hexadecimal representation of a float into a binary representation of this float, that is to say with a dot ( nor than one to transform directly a decimal representation of a float into a binary one).
So I created a function for that purpose.
Before hand, this function transforms the hexadecimal representation into a decimal representation (string) of the input float.
Then there are two problems:
how to transform the part before the dot ?
This part being an integer, it's easy to usebin()how to transform the part after the dot ??
The problem of this transformation has been asked several times on SO, but I didn't understand the solutions, so I wrote my own.
Then, here's the function you wish, Qiang Li:
def hexf2binf(x):
'''Transforms an hexadecimal float with a dot into a binary float with a dot'''
a,_,p = str(float.fromhex(x)).partition('.')
# the following part transforms the part after the dot into a binary after the dot
tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)]
bits = []
pdec = Decimal('.'+p)
for tin in tinies:
if pdec-tin==0:
bits.append('1')
break
elif pdec-tin>0:
bits.append('1')
pdec -= tin
else:
bits.append('0')
pbin = ''.join(bits) # it's the binary after the dot
# the integer before the dot is easily transformed into a binary
return '.'.join((bin(int(a)),pbin))
.
In order to perform verification, I wrote a function to transform the part of a binary float after a dot into its decimal representation:
from decimal import Decimal, getcontext()
getcontext().prec = 500
# precision == 500 , to be large !
tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)]
com = dict((i,tin) for i,tin in enumerate(tinies,1))
def afterdotbinary2float(sbin, com = com):
'''Transforms a binary lying after a dot into a float after a dot'''
if sbin.startswith('0b.') or sbin.startswith('.'):
sbin = sbin.split('.')[1]
if all(c in '01' for c in sbin):
return sum(int(c)*com[i] for i,c in enumerate(sbin,1))
else:
return None
.
.
Finally, applying these functions:
from decimal import Decimal
getcontext().prec = 500
# precision == 500 , to be large !
tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)]
com = dict((i,tin) for i,tin in enumerate(tinies,1))
def afterdotbinary2float(sbin, com = com):
'''Transforms a binary lying after a dot into a float after a dot'''
if sbin.startswith('0b.') or sbin.startswith('.'):
sbin = sbin.split('.')[1]
if all(c in '01' for c in sbin):
return sum(int(c)*com[i] for i,c in enumerate(sbin,1))
else:
return None
def hexf2binf(x):
'''Transforms an hexadecimal float with a dot into a binary float with a dot'''
a,_,p = str(float.fromhex(x)).partition('.')
# the following part transforms the float after the dot into a binary after the dot
tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)]
bits = []
pdec = Decimal('.'+p)
for tin in tinies:
if pdec-tin==0:
bits.append('1')
break
elif pdec-tin>0:
bits.append('1')
pdec -= tin
else:
bits.append('0')
pbin = ''.join(bits) # it's the binary after the dot
# the float before the dot is easily transformed into a binary
return '.'.join((bin(int(a)),pbin))
for n in (45.625 , 780.2265625 , 1022.796875):
print 'n ==',n,' transformed with its method hex() to:'
nhexed = n.hex()
print 'nhexed = n.hex() ==',nhexed
print '\nhexf2binf(nhexed) ==',hexf2binf(nhexed)
print "\nVerification:\nbefore,_,after = hexf2binf(nhexed).partition('.')"
before,_,after = hexf2binf(nhexed).partition('.')
print 'before ==',before,' after ==',after
print 'int(before,2) ==',int(before,2)
print 'afterdotbinary2float(after) ==',afterdotbinary2float(after)
print '\n---------------------------------------------------------------\n'
result
n == 45.625 transformed with its method hex() to:
nhexed = n.hex() == 0x1.6d00000000000p+5
hexf2binf(nhexed) == 0b101101.101
Verification:
before,_,after = hexf2binf(nhexed).partition('.')
before == 0b101101 after == 101
int(before,2) == 45
afterdotbinary2float(after) == 0.625
---------------------------------------------------------------
n == 780.2265625 transformed with its method hex() to:
nhexed = n.hex() == 0x1.861d000000000p+9
hexf2binf(nhexed) == 0b1100001100.0011101
Verification:
before,_,after = hexf2binf(nhexed).partition('.')
before == 0b1100001100 after == 0011101
int(before,2) == 780
afterdotbinary2float(after) == 0.2265625
---------------------------------------------------------------
n == 1022.796875 transformed with its method hex() to:
nhexed = n.hex() == 0x1.ff66000000000p+9
hexf2binf(nhexed) == 0b1111111110.110011
Verification:
before,_,after = hexf2binf(nhexed).partition('.')
before == 0b1111111110 after == 110011
int(before,2) == 1022
afterdotbinary2float(after) == 0.796875
---------------------------------------------------------------
.
For the two numbers:
from decimal import Decimal
tinies = [ Decimal(1) / Decimal(2**i) for i in xrange(1,400)]
com = dict((i,tin) for i,tin in enumerate(tinies,1))
def hexf2binf(x, tinies = tinies):
'''Transforms an hexadecimal float with a dot into a binary float with a dot'''
a,_,p = str(float.fromhex(x)).partition('.')
# the following part transforms the float after the dot into a binary after the dot
bits = []
pdec = Decimal('.'+p)
for tin in tinies:
if pdec-tin==0:
bits.append('1')
break
elif pdec-tin>0:
bits.append('1')
pdec -= tin
else:
bits.append('0')
pbin = ''.join(bits) # it's the binary after the dot
# the float before the dot is easily transformed into a binary
return '.'.join((bin(int(a)),pbin))
print hexf2binf('0x1.a000000000000p+2')
print
print hexf2binf('0x1.b5c2000000000p+1')
as a result, this displays:
0b110.1
0b11.011010111000010000000000000000000000010000011111100001111111101001000110110000101101110000000001111110011100110101011100011110011101111010001011111001011101011000000011001111111010111011000101000111100100110000010110001000111010111101110111011111000100100110110101011100101001110011000100000000010001101111111010001110100100101110111000111111001011101101010011011111011001010011111111010101011010110
Copied from this answer and edited per suggestion from Mark Dickinson:
import struct
def float_to_bin(num):
return format(struct.unpack('!I', struct.pack('!f', num))[0], '032b')
def bin_to_float(binary):
return struct.unpack('!f',struct.pack('!I', int(binary, 2)))[0]
print float_to_bin(3.14) yields โ01000000010010001111010111000011โ.
print bin_to_float("11000000001011010111000010100100") yields โ-2.71000003815โ.
I was able to create a program that takes bin decimals as string an returns int decimals!
I used a for loop to start from 1 until the len() of the str+1 to use i number to elevate 2 and, then just keep track of the result with result +=:
def binary_poin_to_number(bin1)->float:
#Try out string slicing here, later
result = 0
for i in range(1,len(bin1)+1):
if bin1[i-1] == '1':
result += 2**-i
return result
One equivalent would be
x=2049
p0 = (x>>24)&255
p1 = (x>>16)&255
p2 = (x>>8)&255
p3 = x&255
def f2b(a):
import math
binary = []
for i in range(100):
a=a*2
if a<1:
binary.append(0)
else:
binary.append(1)
if a==0:
break
a,b=math.modf(a)
binary=''.join(str(d) for d in binary)
return binary[:-1]
It is not a perfect way to do it but it works perfectly. Input any floating value ( ex. 0.25 ) with is divisible by 2, the output will be binary value let's say "10".
Thank you