Why is int(x-1) == x True in Python 3.7 with some values of x?
Let's start by establishing that 5 == 5.0
is True
even though 5
is an int
and 5.0
is a float
. This is by design.
If we keep that in mind, then we can also accept that int(5e+17) == 5e+17
is True
.
Finally, we see that int(5e+17) == int(5e+17-1)
is also True
because of precision errors (Thanks @juanpa.arrivillaga for the link).
Now it is clear why int(5e+17-1) == 5e+17
is True
.
This can be solved by using Decimal
but be sure to initialize it with a string:
from decimal import Decimal
Decimal('5e+17') - 1 == Decimal('5e+17')
# False
Python float
is stored as a double-precision floating point number. They only have 53 bits of precision, so integers larger than 253 stored as floats begin to lose precision. Here's a clear example of how large numbers begin to lose precision:
>>> x = float(2**53-10)
>>> x
9007199254740982.0
>>> for i in range(20):
... print(x+i)
...
9007199254740982.0
9007199254740983.0
9007199254740984.0
9007199254740985.0
9007199254740986.0
9007199254740987.0
9007199254740988.0
9007199254740989.0
9007199254740990.0
9007199254740991.0 <--- 2**53-1
9007199254740992.0 <--- 2**53
9007199254740992.0 <--- NOT 2**53+1
9007199254740994.0 <--- 2**53+2
9007199254740996.0
9007199254740996.0
9007199254740996.0
9007199254740998.0
9007199254741000.0
9007199254741000.0
9007199254741000.0
The above number is approximately 9e+15, so your 1e+17 number is well into loss of precision. You have to add/subtract 16 from floats that large to expect a change in stored value:
>>> x = 1e17
>>> for i in range(20):
... print(f'{x+i:.1f}')
...
100000000000000000.0
100000000000000000.0
100000000000000000.0
100000000000000000.0
100000000000000000.0
100000000000000000.0
100000000000000000.0
100000000000000000.0
100000000000000000.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
100000000000000016.0
Python has functions to convert to and from an exact binary floating point value. The 1 before and 13 hexadecimal digits after the decimal indicate the 53-bit value:
>>> (1e17).hex()
'0x1.6345785d8a000p+56'
>>> print(f"{float.fromhex('0x1.6345785d8a000p56'):.1f}")
100000000000000000.0
Adding one to the 53-bit value:
>>> print(f"{float.fromhex('0x1.6345785d8a001p56'):.1f}")
100000000000000016.0