Binary representation of float in Python (bits not hex)
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'
Float representation of binary in Python (bits not hex)
Using struct.pack
and struct.unpack
:
>>> import struct
>>> n = '00111111100000000000000000000000'
>>> struct.unpack('f', struct.pack('i', int(n, 2)))[0]
1.0
int(.., 2)
to convert the binary representation toint
(base 2)struct.pack('i', ..)
to convert bytes (i
: 32bit int)struct.unpack('f', ...)[0]
to convert bytes back to float.
For other struct
format character, see Format charactes - struct
module documentation.
Float to Binary and Binary to Float in Python
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”.
Float to binary
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.
Converting float.hex() value to binary in Python
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
.)
How do I display the binary representation of a float or double?
C/C++ is easy.
union ufloat {
float f;
unsigned u;
};
ufloat u1;
u1.f = 0.3f;
Then you just output u1.u
. You can adapt this implementation.
Doubles just as easy.
union udouble {
double d;
unsigned long u;
}
because doubles are 64 bit.
Java is a bit easier: use Float.floatToRawIntBits() combined with Integer.toBinaryString() and Double.doubleToRawLongBits combined with Long.toBinaryString().
How I can see the actual bit sequence of 0.1 in Python?
You could use the struct
module to pack the floating-point value into a buffer. The "d"
format string gives an 8-byte double
.
import struct
x = float(0.1)
buf = struct.pack("d", x)
print(buf) # b'\x9a\x99\x99\x99\x99\x99\xb9?'
# Hex dump, for readability
print(" ".join("{:02X}".format(b) for b in buf)) # 9A 99 99 99 99 99 B9 3F
The buffer will reflect the native endianness of your system. You can also experiment with byte-order specifiers in the format string.
You can also re-interpret the same bytes as an integer. To do this, you can use the unsigned integer type of the same size (8 bytes). In this case, it would be the long long
unsigned integer (format string "Q"
):
# Interpret bytes as unsigned integer.
i, = struct.unpack("Q", buf)
print("{:0>16X}".format(i)) # 3FB999999999999A
Finally, if you want, you can interpret the buffer as a double
, and confirm that the value survives a round-trip serialization:
x2, = struct.unpack("d", buf)
print(x2) # 0.1
print(x2 == x) # True
Viewing floating-point representation components
To view the individual components of the floating point number, you can examine parts of the integer equivalent using bitmasks.
import struct
x = -(16 + 4 + 2 + 1 + 0.5)
buf = struct.pack("d", x)
i, = struct.unpack("Q", buf)
Here, x
has a value of -0b10111.1
, or equivalently -0b1.01111 * 2^4
.
There are two important notes about the IEEE 753 representation:
- the exponent has a bias of 1023, so we expect a value of 1027 stored in the exponent.
- the mantissa’s leading
1
is implicit, so we expect the mantissa bits to be01111
.
We can use the appropriate masks for the bit patterns shown in the question, then print in binary
print(format(i, '0>64b'))
SIGN_MASK = 0x8000000000000000
EXPN_MASK = 0x7FF0000000000000
MANT_MASK = 0x000FFFFFFFFFFFFF
print(format(i & SIGN_MASK, '0>1b')[:1])
print(format(i & EXPN_MASK, '0>11b')[:11])
print(format(i & MANT_MASK, '0>52b'))
Results:
1100000000110111100000000000000000000000000000000000000000000000
1
10000000011
0111100000000000000000000000000000000000000000000000
Related Topics
Why Doesn't a Python Dict.Update() Return the Object
Python: Best Practice and Securest Way to Connect to MySQL and Execute Queries
Python: How to Get Stdout After Running Os.System
Replicating Rows in a Pandas Data Frame by a Column Value
How to Create a View Onto a Numpy Array
Split Text Lines in Scanned Document
Remove Trailing Newline from the Elements of a String List
Dangers of Sys.Setdefaultencoding('Utf-8')
Converting String to Int Using Try/Except in Python
Calculate Average of Every X Rows in a Table and Create New Table
Matplotlib Fill Between Multiple Lines
Coalesce Values from 2 Columns into a Single Column in a Pandas Dataframe
Pandas Groupby and Select Rows with the Minimum Value in a Specific Column
How to Login to a Website with Python
How to Clone a Django Model Instance Object and Save It to the Database