Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is int(x-1) == x True in Python 3.7 with some values of x?

Tags:

python

In Python 3.7 int(x-1) == x is True for x = 5e+17 Why is this so and how do I prevent this bug?

To reproduce, paste this into your Python console:

int(5e+17-1) == 5e+17
>True

(I am using int because x is the result of a division and I need to parse it as int.)

like image 957
TSR Avatar asked Mar 06 '19 19:03

TSR


People also ask

How do you define X as an integer in Python?

Type int(x) to convert x to a plain integer. Type long(x) to convert x to a long integer.

What is int true in Python?

In Python True and False are equivalent to 1 and 0. Use the int() method on a boolean to get its int values. int() turns the boolean into 1 or 0. Note: that any value not equal to 'true' will result in 0 being returned.

What is floating point error in Python?

It's a problem caused when the internal representation of floating-point numbers, which uses a fixed number of binary digits to represent a decimal number. It is difficult to represent some decimal number in binary, so in many cases, it leads to small roundoff errors.

What is precision in Python?

Python can handle the precision of floating point numbers using different functions. Most functions for precision handling are defined in the math module. So to use them, at first we have to import the math module, into the current namespace. import math. Now we will see some of the functions for precision handling.


2 Answers

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
like image 167
DeepSpace Avatar answered Sep 24 '22 22:09

DeepSpace


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
like image 32
Mark Tolonen Avatar answered Sep 22 '22 22:09

Mark Tolonen