-2
number = 9.3

while number % 0.5 != 0:
    number -= 0.1

print number

-20.0

I expected number to end up leaving this while loop as 9.0, but somehow it has become -20.0. Note that if I change number to 9.2 it works just as I would expect and becomes 9.0. Some values work such as 9.0, 9.7 and some don't such as 9.4, 9.8. I have no clue why. By the way 9.8 becomes -1204.5 which is even more weird. I can't explain these results. Forgive me if I've missed something basic as I have only just begun to learn computer programming.

user3915477
  • 275
  • 4
  • 11

1 Answers1

0

This is because of floating point precision. If you print number and number % 0.5 in the loop, you will get something like:

9.2 0.2
9.1 0.1
9.0 1.7763568394e-15  # Not equal to 0!
8.9 0.4
8.8 0.3
8.7 0.2
8.6 0.1
8.5 3.5527136788e-15  # Still different from 0!
...

And it happens that when number reaches -20, the solution is exact:

-20.0 0.0

What you should do is, instead of checking equality with 0, check if the result is small enough:

EPSILON = 0.0001
number = 9.3
while number % 0.5 > EPSILON:
    number -= 0.1
print number

And the result will be 9.0.


But if you want a cleaner solution which does not involve loops, this will always work:

import math
number = 0.5 * math.floor(2.0 * number)
julienc
  • 19,087
  • 17
  • 82
  • 82
  • No, you should really just avoid accumulating roundoff in the first place. – tmyklebu Aug 06 '14 at 18:21
  • @tmyklebu I disagree: maybe the OP does something else than a simple substraction in its loop that require the temporary value. And in every case, answering with a totally different solution would not help him either to understand the problem. – julienc Aug 06 '14 at 18:23
  • 1
    Disagree all you like, but don't advocate a broken solution based on comparison against an arbitrarily-chosen epsilon. You can give a roundoff-error analysis of repeated subtraction to justify a particular (much tighter) choice of epsilon, or you can avoid the roundoff entirely. Also note that your method will likely break if 0.1 is replaced with a number whose floating-point representation is larger than the user's naive expectation. – tmyklebu Aug 06 '14 at 18:27
  • @tmyklebu Edited my answer with a cleaner one-liner – julienc Aug 06 '14 at 18:32