How does the module operator work (%)?

Asked

Viewed 670 times

2

I have a question about module operation. Why 10 % 8 returns 2?

I couldn’t find it in the documentation.

  • That one reply does not specifically deal with module, it is about Python and Ocaml but explains why the difference in results.

  • https://answall.com/q/93092/70

1 answer

3

The relevant documentation is here, and says the operator % returns the rest of the division of the first number by the second ("The % (module) Operator yields the remainder from the Division of the first argument by the Second").

That is to say, 10 % 8 returns 2 because by dividing 10 by 8, the rest of the division is 2.

The documentation also mentions that "The Operator module Always yields a result with the same Sign as its Second operand (or zero)" (the operator % always returns a value whose signal is the same as the second operand (or zero)). So when there are negative numbers involved in the calculation, the results can be... "odd":

print(-10 % 8) # 6
print(10 % -8) # -6
print(-10 % -8) # -2

The documentation mentions that the result of x % y must be such that x == (x // y) * y + (x % y) - whereas // is the full division operator, which rounds off the result of x / y using the same logic of floor.

It is worth remembering that there is also builtin divmod, whose results use this operator. Basically, divmod(x, y) returns the tuple (x // y, x % y).

And in the module math there are still functions remainder and fmod, that depending on the case may return different results from % (read the documentation to understand how each one works). Examples:

from math import remainder, fmod
def divs(x, y):
    print(f'\n{x} % {y} = {x % y:>15.2f}', )
    print(f'remainder({x}, {y}) = {remainder(x, y):>5.2f}')
    print(f'fmod({x}, {y}) = {fmod(x, y):>10.2f}')

divs(10, 8)
divs(-10, 8)
divs(10, -8)
divs(-10, -8)

divs(2.7, 1)
divs(-2.7, 1)
divs(2.7, -1)
divs(-2.7, -1)

The exit is:

10 % 8 =            2.00
remainder(10, 8) =  2.00
fmod(10, 8) =       2.00

-10 % 8 =            6.00
remainder(-10, 8) = -2.00
fmod(-10, 8) =      -2.00

10 % -8 =           -6.00
remainder(10, -8) =  2.00
fmod(10, -8) =       2.00

-10 % -8 =           -2.00
remainder(-10, -8) = -2.00
fmod(-10, -8) =      -2.00

2.7 % 1 =            0.70
remainder(2.7, 1) = -0.30
fmod(2.7, 1) =       0.70

-2.7 % 1 =            0.30
remainder(-2.7, 1) =  0.30
fmod(-2.7, 1) =      -0.70

2.7 % -1 =           -0.30
remainder(2.7, -1) = -0.30
fmod(2.7, -1) =       0.70

-2.7 % -1 =           -0.70
remainder(-2.7, -1) =  0.30
fmod(-2.7, -1) =      -0.70

To learn more, read here, here, here and here.


It is worth remembering that other languages can give different results when there are negative and/or floating point numbers. Follow an example in Javascript, just to stay in a language:

console.log(10 % 8); // 2
console.log(-10 % 8); // -2
console.log(10 % -8); // 2
console.log(-10 % -8); // -2

console.log((2.7 % 1).toFixed(2)); // 0.70
console.log((-2.7 % 1).toFixed(2)); // -0.70
console.log((2.7 % -1).toFixed(2)); // 0.70
console.log((-2.7 % -1).toFixed(2)); // -0.70

Note that not all results are equal to Python. That’s because the definition of "rest of the division" is kind of "naive and limited". It works very well for positive numbers, but when negative numbers are involved, everything gets complicated. There are variants of this definition, which cause the result signal to end up being the same as the dividend or divisor (and which are explained in this article, in which there is even a comparative table between several languages, showing how each implements the %).

  • 3

    I consider it a mistake of documentation to mix the concept of module with split rest (and several languages do this). Module is "clock arithmetic", cyclical, rest is something else. In the negatives makes all the difference, as you mentioned. And this has implications for a number of practical applications.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.