Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite while loop when checking floats for equality [duplicate]

I'm having an infinite loop when I run this code:

intensity = 0.50
while intensity != 0.65:
    print(intensity)
    intensity = intensity + 0.05

intensity values should be like 0.50 -> 0.55 -> 0.60 -> 0.65 then it should gets out of the loop. Why the program is doing an infinite loop instead?

like image 884
Tote99 Avatar asked Apr 18 '20 04:04

Tote99


4 Answers

Because of floating point imprecision, you may not end up with exactly 0.65.

To solve this, use < rather than != (a):

intensity = 0.50
while intensity < 0.65:
    print(intensity)
    intensity = intensity + 0.05

The output of that (showing what I mean by imprecision as well) is:

0.5
0.55
0.6000000000000001

If you kept going, you'd see:

0.6500000000000001
0.7000000000000002
0.7500000000000002
0.8000000000000003

which is why it's never equal to 0.65.


(a) You may be wondering what the situation would be if the next value was 0.6499...9 rather than 0.650..1, and you would probably be right to be concerned.

While using < will fix the latter case, you'll almost certainly get one iteration too many for the former case.

You can fix that by a number of possible different strategies, some of which are:

  • round the number to the correct number of decimals before comparing, such as round(intensity, 2).
  • compare it with a "close enough" check, such as the absolute difference being less that 10-4 (for example) - something like if abs(intensity - 0.65) < 0.0001.
  • use integral values exclusively and scale the value as needed (e.g., initialising intensity to 50, adding 5, comparing to 65, and printing round(intensity / 100, 2).

The Python documentation has an interesting article that you can read to further understand these limitations.

like image 183
paxdiablo Avatar answered Oct 22 '22 02:10

paxdiablo


while True:
    if intensity>0.65:
        break
    print(intensity)
    intensity+=0.05
like image 33
Darkknight Avatar answered Oct 22 '22 01:10

Darkknight


This is due to the way Python (and some other languages like C) handle floating-point provisions. See this or this. In general, you should avoid floating point loop counters.

If you still wanted to use one, you can round it off:

intensity = 0.50
while round(intensity,2) != 0.65:
    print(round(intensity,2))
    intensity = intensity + 0.05
like image 25
Sunny Patel Avatar answered Oct 22 '22 02:10

Sunny Patel


Look at your output:

0.5
0.55
0.6000000000000001
0.6500000000000001
0.7000000000000002

0.05 cannot be exactly represented with a terminating binary float number.

See is floating point math broken?

If you want to check whether the value is close, then simply set your desired tolerance:

while abs(intensity - 0.65) < my_tolerance:

Better yet, use the built-in function, setting either relative or absolute tolerance:

isclose(intensity, 0.65, rel_tol=1e-9, abs_tol=0.0)
like image 40
Prune Avatar answered Oct 22 '22 02:10

Prune