Floating point numbers are implemented as binary64
according to IEEE 754 (as in virtually all programming languages).
This standard gives 52 bits to the "significand / fraction" (approximately 16 decimal digits of accuracy), 11 bits to the exponent and 1 bit to the sign (plus or minus):
In particular, a number like 0.4
can not be represented as
(1 + f) * 2**(exponent)
for some fraction in base 2 and an exponent that can be represented with 11 bits (-1022 through 1023).
Viewing 0.4
in hex for example:
>>> (0.4).hex()
'0x1.999999999999ap-2'
we see the best approximation in our set of numbers is
+ 2**(-2) * (1 + 0x999999999999a/ float(2**52))
Trying to represent this in base 2, we have
2**(-2) * (1 + 0.6)
but 0.6 = 9/15 = 1001_2/1111_2
written in base 2 has a repeating string of four binary digits
0.1001100011000110001...
so can never be represented using a finite number of binary digits.
A bit more in depth
So we can "unpack" 0.4
>>> import struct
>>> # 'd' for double, '>' for Big-endian (left-to-right bits)
>>> float_bytes = struct.pack('>d', 0.4)
as 8 bytes (1 byte is 8 bits)
>>> float_bytes
'?\xd9\x99\x99\x99\x99\x99\x9a'
or as 16 hex digits (1 hex digit is 4 bits, since 2**4 == 16
)
>>> ''.join(['%2x' % (ord(digit),) for digit in float_bytes])
'3fd999999999999a'
or as all 64 bits in their glory
>>> float_bits = ''.join(['%08d' % (int(bin(ord(digit))[2:]),)
... for digit in float_bytes])
>>> float_bits
'0011111111011001100110011001100110011001100110011001100110011010'
From there, the first bit is the sign bit:
>>> sign = (-1)**int(float_bits[0], 2)
>>> sign
1
The next 11 bits are the exponent (but shifted by 1023, a convention
of binary64
):
>>> exponent = int(float_bits[1:1 + 11], 2) - 1023
>>> exponent
-2
The final 52 bits are the fractional part
>>> fraction = int(float_bits[1 + 11:1 + 11 + 52], 2)
>>> fraction
2702159776422298
>>> hex(fraction)
'0x999999999999a'
Putting it all together
>>> sign * 2**(exponent) * (1 + fraction / float(2**52))
0.4
float
repr
s for some values. Straight from the horse's mouth, so to speak. – Whereas