Modulo for negative dividends in Python

旧时模样 提交于 2019-11-29 00:15:29

In Python, modulo is calculated according to two rules:

  • (a // b) * b + (a % b) == a, and
  • a % b has the same sign as b.

Combine this with the fact that integer division rounds down (towards −∞), and the resulting behavior is explained.

If you do -8 // 5, you get -1.6 rounded down, which is -2. Multiply that by 5 and you get -10; 2 is the number that you'd have to add to that to get -8. Therefore, -8 % 5 is 2.

In Python, a // b is defined as floor(a/b), as opposed to most other languages where integer division is defined as trunc(a/b). There is a corresponding difference in the interpretation of a % b = a - (a // b) * b.

The reason for this is that Python's definition of the % operator (and divmod) is generally more useful than that of other languages. For example:

def time_of_day(seconds_since_epoch):
    minutes, seconds = divmod(seconds_since_epoch, 60)
    hours, minutes = divmod(minutes, 60)
    days, hours = divmod(hours, 24)
    return '%02d:%02d:%02d' % (hours, minutes, seconds)

With this function, time_of_day(12345) returns '03:25:45', as you would expect.

But what time is it 12345 seconds before the epoch? With Python's definition of divmod, time_of_day(-12345) correctly returns '20:34:15'.

What if we redefine divmod to use the C definition of / and %?

def divmod(a, b):
    q = int(a / b)   # I'm using 3.x
    r = a - b * q
    return (q, r)

Now, time_of_day(-12345) returns '-3:-25:-45', which isn't a valid time of day. If the standard Python divmod function were implemented this way, you'd have to write special-case code to handle negative inputs. But with floor-style division, like my first example, it Just Works.

The rationale behind this is really the mathematical definition of least residue. Python respects this definition, whereas in most other programming language the modulus operator is really more like a 'reaminder after division' operator. To compute the least residue of -5 % 11, simply add 11 to -5 until you obtain a positive integer in the range [0,10], and the result is 6.

When you divide ints (-2/5)*5 does not evaluate to -2, as it would in the algebra you're used to. Try breaking it down into two steps, first evaluating the part in the parentheses.

  1. (-2/5) * 5 = (-1) * 5
  2. (-1) * 5 = -5

The reason for step 1 is that you're doing int division, which in python 2.x returns the equivalent of the float division result rounded down to the nearest integer.

In python 3 and higher, 2/5 will return a float, see PEP 238.

Check out this BetterExplained article and look @ David's comment (No. 6) to get what the others are talking about.

Since we're working w/ integers, we do int division which, in Python, floors the answer as opposed to C. For more on this read Guido's article.

As for your question:

>>> 8 % 5  #B'coz (5*1) + *3* = 8
3
>>> -8 % 5 #B'coz (5*-2) + *2* = -8
2

Hope that helped. It confused me in the beginning too (it still does)! :)

Say -a % b needs to be computed. for ex. r= 11 % 10 find the next number after 11 that is perfectly divisible by 10 i.e on dividing that next number after 11 gives the remainder 0.

In the above case its 20 which on dividing by 10 gives 0. Hence, 20-11 = 9 is the number that needs to be added to 11.

The concept if 60 marbles needs to be equally divided among 8 people, actually what you get after dividing 60/8 is 7.5 since you can'nt halve the marbles, the next value after 60 that is perfectly divisible by 8 is 64. Hence 4 more marbles needs to be added to the lot so that everybody share the same joy of marbles.

This is how Python does it when negatives numbers are divided using modulus operator.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!