Question

I'm having a problem with modulus on a floating point number in Python. This code:

...
print '(' + repr(olddir) + ' + ' + repr(self.colsize) + ') % (math.pi*2) = ' + repr((olddir+self.colsize)
...

Prints:

(6.281876310240881 + 0.001308996938995747) % (math.pi*2) = 2.9043434324194095e-13

I know floating point numbers aren't precise. But I can't get this to make any sense.

I don't know if it is in any way related but Google Calculator can't handle this calculation either. This is the output from Google Calculator:

(6.28187631024 + 0.001308996939) % (pi * 2) = 6.28318531

What is causing this calculation error? And how can I avoid it in my Python program?

Was it helpful?

Solution

Using str() to print a floating point number actually prints a rounded version of the number:

>>> print repr(math.pi)
3.1415926535897931
>>> print str(math.pi)
3.14159265359

So we can't really reproduce your results, since we don't know the exact values you are doing the computation with. Obviously, the exact value of olddir+self.colsize is slightly greater than 2*math.pi, while the sum of the rounded values you used in Google Calculator is slightly less than 2*math.pi.

OTHER TIPS

The difference between str and repr

>>> import scipy
>>> pi = scipy.pi
>>> str(pi)
'3.14159265359'
>>> repr(pi)
'3.1415926535897931'

str truncates floating point numbers to 12 digits, where repr gives the internal representation (as a string).

EDIT: So in summary, the problem arose because you rounded prematurely and are calculating the modulus of something via a number that's very close to it. With floating point numbers, rounding is inevitably involved in converting decimal numbers into binary.

First, do an example of how rounding hurts you with actual math (not floating point math). Look at (3.14+3.14) % (3.14+3.14), which is obviously zero. Now what would happen if we rounded the digits to one decimal digit first on one side? Well (3.1+3.1) % (3.14+3.14) = 6.2 % (6.28) = 6.2 (what google gave you). Or if you did round(3.14159,5) + round(3.14159,5) % (3.14159 + 3.14159) = 6.2832 % 6.28318 = 2e-5.

So in by rounding to N digits (by using str which effectively rounds the numbers), your calculation is only accurate to less than N digits. To have this work going forward force rounding at some higher digit (keeping two calculated digits for safety) is necessary. E.g., str rounds at digit 12, so maybe we should round at digit 10.

>>> round(6.28187631024 + 0.001308996939,10) % (round(pi * 2,10))
0
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top