Reputation: 4332
Does anyone know a good way in Python to check if a number is divisible by another in floating point in python?
The first thing I tried was ...
3.5 % 0.1 == 0.0
But this returns False
so then maybe
3.5 % 0.1 >= 1e-6
But also False
... bummer ... it turns out that
3.5 % 0.1
>> 0.099999999924
So then this works:
LAMBDA = 1e-9
def is_divisible_by(x, y):
m = x % y
dy = abs(y - m)
return m < LAMBDA or dy < LAMBDA
is_divisible_by(3.5, 0.1)
But this seems dangerous because I have to pick a LAMBDA. What about if y = LAMBDA / 2
...
is_divisible_by(LAMBDA/2, (LAMBDA/2) + 1e-10)
>>> True
So then
def is_divisible_by(x, y):
l = y * 1e-2
m = x % y
dy = abs(y - m)
return m < l or dy < l
is_divisible_by(3.5 * 1e-10, 0.1 * 1e-10)
>>> True
is_divisible_by(0.21, 0.211)
>>> True
Bummer.
Is there anyway to solve this without going down a massive rabbit hole?
Upvotes: 3
Views: 1901
Reputation: 31
A potential solution is to multiply both floats by a "sufficiently large" scaling factor and then cast (round) to integers.
def check_floats_divisible(x: float, y: float, scaling_factor: float = 1e6):
scaled_x = int(x * scaling_factor)
scaled_y = int(y * scaling_factor)
return (scaled_x % scaled_y) == 0
check_floats_divisible(3.5, 0.1)
>>> True
Notes:
Upvotes: 1
Reputation: 1742
floating point numbers are "fuzzy". A good high-level mental model for floating point numbers is that they represent a small range of numbers (e.g 1.5 really means some number between 1.4999 and 1.5002). Because of this, there is not good way to check if one is divisible by another. Instead, to check if non-integer numbers are divisible by each other, you might want to use rational-type numbers. In python, there's a module for this, called Fraction. You can use it like this
from fractions import Fraction
a = Fraction(35,10) # 3.5
b = Fraction(1,10) # .1
a%b # evaluates to Fraction(0, 1)
Another answer mentioned the decimal python module. Fraction and decimal are interoperable.
from fractions import Fraction
from decimal import Decimal
a = Fraction(Decimal('3.5')) # 3.5
b = Fraction(Decimal('0.1)) # 0.1
a%b # evaluates to Fraction(0, 1)
I'm going to advocate for using Fraction as it is a bit more flexible
from fractions import Fraction
from decimal import Decimal
c = Decimal('1')
d = Decimal('.3')
c/d # evaluates to Decimal('3.333333333333333333333333333')
c/d*d # evaluates to Decimal('0.9999999999999999999999999999')
c = Fraction(c)
d = Fraction(d)
c/d # evaluates to Fraction(10, 3)
c/d*d # evaluates to Fraction(1, 1)
Upvotes: 3
Reputation: 37364
Depending on the source of your floating point numbers, the decimal
module might be useful.
>>> import decimal
>>> decimal.Decimal("3.5") % decimal.Decimal("0.1")
Decimal('0.0')
Upvotes: 6