The following python code calculates the number of iterations to do stuff based on some variables.
# a - b - c is always a multiple of d.
i = (a - b - c) / d
while i:
# do stuff
i -= 1
The variables will all be of the same type, that is only ints or floats or whatever. My concern is whether it will work correctly if the values are floats. I know enough to always consider the pitfalls of relying on exact float values. But I can’t tell if the above is dangerous or not. I can use i = int(round((a - b - c) / d)), but I am curious as to understand floats better.
It all comes down to the following: a - b - c is an exact multiple of d. So I am relying on (a-b-c)/d to become a value i that I can subtract 1 from and get the expected number of iterations in the while loop, with the implied assumption that i == 0 becomes true. That is, can calculated multiples like this be decremented by 1 to reach exactly 0?
I would like to not only know if it is unsafe, but more importantly, what do I need to understand about floating point to resolve a question like this? If someone knows decisively whether this is safe or not, would it be possible to explain how so?
You can use the decimal module to get an idea of what “hides” between a floating point number such as
0.3:Note that Python 2.7 changed how floating point numbers are written (how
repr(f)works) so that it now shows the shortest string that will give the same floating point number if you dofloat(s). This means thatrepr(0.3) == '0.3'in Python 2.7, butrepr(0.3) == '0.29999999999999999'in earlier versions. I’m mentioning this since it can confuse things further when you really want to see what’s behind the numbers.Using the decimal module, we can see the error in a computation with floats:
Here we might expect
(2.0 - 1.1) / 0.3 == 3.0, but there is a small non-zero difference. However, if you do the computation with normal floating point numbers, then you do get zero:The result is rounded somewhere along the way since 1.85e-16 is non-zero:
I’m unsure exactly where this rounding takes place.
As for the loop termination in general, then there’s one clue I can offer: for floats less than 253, IEEE 754 can represent all integers:
The space between representable numbers is 2 from 253 to 254, as shown above. But if your
iis an integer less than 253, theni - 1will also be a representable integer and you will eventually hit0.0, which is considered false in Python.