The modulo operation on negative numbers in Python

PythonModuloNegative Number

Python Problem Overview


I've found some strange behaviour in Python regarding negative numbers:

>>> -5 % 4
3

Could anyone explain what's going on?

Python Solutions


Solution 1 - Python

Unlike C or C++, Python's modulo operator (%) always return a number having the same sign as the denominator (divisor). Your expression yields 3 because

> (-5) / 4 = -1.25 --> floor(-1.25) = -2 > > (-5) % 4 = (-2 × 4 + 3) % 4 = 3.

It is chosen over the C behavior because a nonnegative result is often more useful. An example is to compute week days. If today is Tuesday (day #2), what is the week day N days before? In Python we can compute with

return (2 - N) % 7

but in C, if N ≥ 3, we get a negative number which is an invalid number, and we need to manually fix it up by adding 7:

int result = (2 - N) % 7;
return result < 0 ? result + 7 : result;

(See http://en.wikipedia.org/wiki/Modulo_operator for how the sign of result is determined for different languages.)

Solution 2 - Python

Here's an explanation from Guido van Rossum:

http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html

Essentially, it's so that a/b = q with remainder r preserves the relationships b*q + r = a and 0 <= r < b.

Solution 3 - Python

In python, modulo operator works like this.

>>> mod = n - math.floor(n/base) * base

so the result is (for your case):

mod = -5 - floor(-1.25) * 4
mod = -5 - (-2*4)
mod = 3

whereas other languages such as C, JAVA, JavaScript use truncation instead of floor.

>>> mod = n - int(n/base) * base

which results in:

mod = -5 - int(-1.25) * 4
mod = -5 - (-1*4)
mod = -1

If you need more information about rounding in python, read this.

Solution 4 - Python

As pointed out, Python modulo makes a well-reasoned exception to the conventions of other languages.

This gives negative numbers a seamless behavior, especially when used in combination with the // integer-divide operator, as % modulo often is (as in math.divmod):

for n in range(-8,8):
    print n, n//4, n%4

Produces:

 -8 -2 0
 -7 -2 1
 -6 -2 2
 -5 -2 3

 -4 -1 0
 -3 -1 1
 -2 -1 2
 -1 -1 3

  0  0 0
  1  0 1
  2  0 2
  3  0 3

  4  1 0
  5  1 1
  6  1 2
  7  1 3
  • Python % always outputs zero or positive*
  • Python // always rounds toward negative infinity

* ... as long as the right operand is positive. On the other hand 11 % -10 == -9

Solution 5 - Python

There is no one best way to handle integer division and mods with negative numbers. It would be nice if a/b was the same magnitude and opposite sign of (-a)/b. It would be nice if a % b was indeed a modulo b. Since we really want a == (a/b)*b + a%b, the first two are incompatible.

Which one to keep is a difficult question, and there are arguments for both sides. C and C++ round integer division towards zero (so a/b == -((-a)/b)), and apparently Python doesn't.

Solution 6 - Python

Other answers, especially the selected one have clearly answered this question quite well. But I would like to present a graphical approach that might be easier to understand as well, along with python code to perform normal mathematical modulo in python.

Python Modulo for Dummies

Modulo function is a directional function that describes how much we have to move further or behind after the mathematical jumps that we take during division over our X-axis of infinite numbers. So let's say you were doing 7%3

enter image description here

So in forward direction, your answer would be +1, but in backward direction-

enter image description here

your answer would be -2. Both of which are correct mathematically.

Similarly, you would have 2 moduli for negative numbers as well. For eg: -7%3, can result both in -1 or +2 as shown -

enter image description here

Forward direction


enter image description here

Backward direction


In mathematics, we choose inward jumps, i.e. forward direction for a positive number and backward direction for negative numbers.

But in Python, we have a forward direction for all positive modulo operations. Hence, your confusion -

>>> -5 % 4 
3

>>> 5 % 4
1

Here is the python code for inward jump type modulo in python:

def newMod(a,b):
    res = a%b
    return res if not res else res-b if a<0 else res

which would give -

>>> newMod(-5,4)
-1

>>> newMod(5,4)
1

Many people would oppose the inward jump method, but my personal opinion is, that this one is better!!

Solution 7 - Python

Modulo, equivalence classes for 4:

  • 0: 0, 4, 8, 12... and -4, -8, -12...
  • 1: 1, 5, 9, 13... and -3, -7, -11...
  • 2: 2, 6, 10... and -2, -6, -10...
  • 3: 3, 7, 11... and -1, -5, -9...

Here's a link to modulo's behavior with negative numbers. (Yes, I googled it)

Solution 8 - Python

I also thought it was a strange behavior of Python. It turns out that I was not solving the division well (on paper); I was giving a value of 0 to the quotient and a value of -5 to the remainder. Terrible... I forgot the geometric representation of integers numbers. By recalling the geometry of integers given by the number line, one can get the correct values for the quotient and the remainder, and check that Python's behavior is fine. (Although I assume that you have already resolved your concern a long time ago).

Solution 9 - Python

It's also worth to mention that also the division in python is different from C: Consider

>>> x = -10
>>> y = 37

in C you expect the result

0

what is x/y in python?

>>> print x/y
-1

and % is modulo - not the remainder! While x%y in C yields

-10

python yields.

>>> print x%y
27

You can get both as in C

The division:

>>> from math import trunc
>>> d = trunc(float(x)/y)
>>> print d
0

And the remainder (using the division from above):

>>> r = x - d*y
>>> print r
-10

This calculation is maybe not the fastest but it's working for any sign combinations of x and y to achieve the same results as in C plus it avoids conditional statements.

Solution 10 - Python

You can use:

result = numpy.fmod(x,y)

it will keep the sign , see numpy fmod() documentation.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfachaView Question on Stackoverflow
Solution 1 - PythonkennytmView Answer on Stackoverflow
Solution 2 - PythonKevinView Answer on Stackoverflow
Solution 3 - PythonMunkhbold EnkhturView Answer on Stackoverflow
Solution 4 - PythonBob SteinView Answer on Stackoverflow
Solution 5 - PythonDavid ThornleyView Answer on Stackoverflow
Solution 6 - PythonDeekshantView Answer on Stackoverflow
Solution 7 - PythonwheatiesView Answer on Stackoverflow
Solution 8 - PythonjoserView Answer on Stackoverflow
Solution 9 - PythonbebboView Answer on Stackoverflow
Solution 10 - Pythonרועי שמשView Answer on Stackoverflow