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

Tags:

Python