Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modulo for negative dividends in Python

Tags:

python

modulo

Been looking through other answers and I still don't understand the modulo for negative numbers in python

For example the answer by df

x == (x/y)*y + (x%y)

so it makes sense that (-2)%5 = -2 - (-2/5)*5 = 3

Doesn't this (-2 - (-2/5)*5) =0 or am I just crazy? Modulus operation with negatives values - weird thing?

Same with this negative numbers modulo in python Where did he get -2 from?

Lastly if the sign is dependent on the dividend why don't negative dividends have the same output as their positive counterparts?

For instance the output of

print([8%5,-8%5,4%5,-4%5])

is

[3, 2, 4, 1]
like image 684
canyon289 Avatar asked Apr 08 '12 14:04

canyon289


2 Answers

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.

like image 149
Taymon Avatar answered Sep 28 '22 12:09

Taymon


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.

like image 26
dan04 Avatar answered Sep 28 '22 13:09

dan04