0

I am struggling to understand the modulus part of this code.

I am trying to ONLY print the num when the modulus is 0, that is to say it's an even number. If I set number as 100 and rate as 1.5 I'll get results. However, if I change the rate value to anything other than .25 .5 .75 I get nothing returned.

Here's an example: If I use 100 as the number and 1.4 as the rate I should get 7.14.21 etc returned as 1.4 goes into these numbers evenly. However, it doesn't output.

Initial searches tells me modulus won't work for decimals yet it does for .5 increments.

Any input greatly appreciated.

def findnum(number, rate): 
    for num in range(pinumber):
        if num %(rate) == 0:
            if num != 0:                 
                print(num)
hrokr
  • 3,276
  • 3
  • 21
  • 39
  • Why did you tag modulus.io? – drum Aug 28 '21 at 14:32
  • Your expression `num % rate` is defined as the remainder of `num / rate` where `/` does floating-point division. 1.4 cannot be accurately represented as a float and so the remainder will often have roundoff error. You are comparing it to zero, which goes against the rule of thumb *Do not compare floats for equality*. .25 .5, .75 all work because they are all powers of 2: 2**-2, 2**-1, or sums of powers of 2: 3*2**-2. Consider using `math.isclose(num, 0)` instead of `num==0`. – BoarGules Aug 28 '21 at 14:49
  • 1
    As an aside, if you do `for num in range(1. number)` you don't need the `if num != 0` at the bottom. And change that "pinumber" to "number". It just shows that you didn't run the script you posted for us. Add the call `findnum(100, 1.4)` so that the script fully demonstrates the error. Don't make us work for it! – tdelaney Aug 28 '21 at 14:56
  • You're getting a lot of answers but I'd say Python documentation has the definitive answer since it's, well the documentaion. "... For this reason, function fmod() is generally preferred when working with floats, while Python’s x % y is preferred when working with integers." https://docs.python.org/3/library/math.html#number-theoretic-and-representation-functions – hrokr Aug 28 '21 at 15:09
  • Does this answer your question? [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Kelly Bundy Aug 28 '21 at 15:19

3 Answers3

2

When deviding with decimal numbers, there is always the problem of precision errors. That means, that 7 % 1.4 isn't 0 but 4.440892098500626e-16, for your code to work you must use the decimal module whitch stores the numbers as strings.

from decimal import Decimal
def findnum(number, rate): 
    for num in range(number):
        if num % Decimal(str(rate)) == 0:
            if num != 0:                 
                print(num)
Bertik23
  • 401
  • 4
  • 9
  • 1
    As an example, 1.4 itself can't be represented exactly with a float. Its really 1.399999999999999911182158029987476766109466552734375. – tdelaney Aug 28 '21 at 14:53
0

Operations on floating point numbers are not exact. For this reason 7 % 1.4 will not give precisely 0, but a number close to 0. If you replace in your code num % rate == 0 with something like num % rate < tolerance where tolerance is a small positive number (say, tolerance = 10**(-10)), it should work in most cases. However, this will still bring incorrect results for some values (try e.g. num = 5 and rate = 5/3) because Python may overestimate the value of num/rate, and then num % rate will be calculated as close to rate and not to 0. So, a safer solution is to use a condition such as abs(num/rate - round(num/rate)) < tolerance.

bb1
  • 7,174
  • 2
  • 8
  • 23
0

Due to decimal precision in python, 7 % 1.4 doesn't give 0 precisely.

So, one approach would be using the 'isclose' function in the standard Math library to compensate for decimal precision.

if isclose(num % rate, abs_tol=1e-9):
    ...

Or, you may use the Decimal library, which offers much better precision, but requires a string conversion.