Reputation: 22254
How can I visualize the floating number 0.1
in the 64 bit binary format stored in memory in Python, like in the image? A Python code which can show the sign bit, exponent bits, and precision bits like (1,01010110111,1010101...).
A float number needs to be represented in binary bits, and as far as I could understand from the articles, it is ± p × 2**e
where ± is i 1 bit, p is 52 bits precision, and e is exponent in 11 bits.
I suppose the reason for (3 * 0.1 == 0.3) is False
is because 0.1
cannot be represented in 64 bits and it is 0.1000000000000000055511151231257827021181583404541015625
in Python in decimal.
print(3 * 0.1 == 0.3) # False
print(0.3 * 1 == 0.3) # True
print("%0.55f" % (1.0 / 10))
-----
0.1000000000000000055511151231257827021181583404541015625
print("%0.55f" % (0.1000000000000000055511151231257827021181583404541015625 - 0.1))
-----
0.0000000000000000000000000000000000000000000000000000000
However, still not able to understand how it works and would like to see how it looks like in the binary format.
Upvotes: 1
Views: 764
Reputation: 4673
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
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:
1
is implicit, so we expect the mantissa bits to be 01111
.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
Upvotes: 1