Reputation: 1214
I would like to check if a float is a multiple of another float, but am running into issues with machine precision. For example:
t1 = 0.02
factor = 0.01
print(t1%factor==0)
The above outputs True
, but
t2 = 0.030000000000000002
print(round(t2,5)%factor==0)
This outputs False
. At some points in my code the number I am checking develops these machine precision errors, and I thought I could fix the issue simply by rounding it (I need 5 decimal places for later in my code, but it also doesn't work if I just round it to 2 decimal places).
Any ideas why the above check round(t2,5)%factor==0
doesn't work as expected, and how I can fix it?
Upvotes: 1
Views: 828
Reputation: 1534
Generally, floats in Python are... messed up, for the lack of a better word. And they can act in very unexpected ways. (You can read more about that behaviour here.)
For your goal however, a better way is this:
t2 = 0.03000003
factor = 0.01
precision = 10000 # 4 digits
print(int(t2*precision)%int(factor*precision)==0)
Moving the maths to an integer-based calculation solves most of those issues.
Upvotes: 0
Reputation: 1603
You should use the decimal module. The decimal module provides support for fast correctly-rounded decimal floating point arithmetic.
import decimal
print( decimal.Decimal('0.03') % decimal.Decimal('0.01') == decimal.Decimal('0') )
Gives :
True
Upvotes: 2
Reputation: 1525
It doesn't work as expected because checking floats for equality almost never works as expected. A quick fix would be to use math.isclose
. This allows you to adjust your tolerance as well. Remember that when doing arithmetic mod r, r is equivalent to 0, so you should check if you're close to 0 or r.
import math
t1 = 0.02
factor = 0.01
res = t1 % factor
print(math.isclose(res, 0) or math.isclose(res, factor))
This is pretty quick and dirty and you will want to make sure your tolerances are working correctly and equivalently for both of those checks.
Upvotes: 3