Python makes working with numbers easy since it's a loosely typed language. One of the arithmetic operators you can use when working with numbers is the modulo (%) operator.
The function of the operator is straightforward. It returns the remainder of the division of two numbers.
In this post, we will discuss the mathematical significance of the operator and how to use it in Python with examples.
Origins and Mathematical Significance of the Modulo Operator
The word "modulo" originates from modular arithmetic, the modern version of which was developed by Gauss in 1801.
Modular arithmetic is a type of math where instead of working with regular numbers like 1, 2, and 3, we work with remainders when we divide numbers by a fixed number called the modulus.
For example, if we're doing "mod 5" arithmetic, we only work with remainders 0, 1, 2, 3, and 4 when dividing by 5.
So, we add, subtract, multiply, or divide in modular arithmetic just like in regular arithmetic. But we take the remainder when the result is divided by the modulus.
Modular arithmetic is useful in many fields, including number theory and cryptography. For example, it can help us solve problems related to repeating patterns, encryption, and prime numbers.
The modulo operator can help when you compare a number with the modulus and get an equivalent number inside the modulus's range.
Let's understand this with a straightforward example. Let's say you want to determine what time it'll be seven hours after 8 AM.
Now, plainly adding seven to eight will give you 15, which isn't a valid time on a 12-hour clock. So, you must take the result of the addition (15) and do the modulo operation on it to get the appropriate value.
Here's what this would look like when worked out on paper:
8 AM + 7 hours = 15 o' clock = 15 mod 12 = 3
So, 15 mod 12 returns three, meaning seven hours past 8 AM is 3 PM.
If you think about this problem in a "mod 12" context, you will realize that the values 15 and 3 are equivalent in this context – the hour hand rests at the same position at 15:00 and 3:00.
For this reason, modulo arithmetic is also called clock arithmetic. Additionally, modulo arithmetic describes this relationship between the equivalent numbers with a simple equation:
a ≡ b (mod n)
Which reads, "a and b are congruent modulo n."
In other words, in the context of mod n, the values of a and b are the same since they have the same remainder when divided by n.
Let's plug the values of the example we discussed earlier into this equation. Here's what this would look like:
15 ≡ 3 (mod 12)
This equation reads, "15 and 3 are congruent modulo 12," and 15 and 3 have the same remainder when divided by 12. So, this way, the numbers 15 and 3 are equivalent in mod 12.
It's easy to confirm this with simple division:
15 / 12 = 1 R 3
3 / 12 = 0 R 3
Now that you understand how the modulo operation works in arithmetic let's see how you can use the operation in Python.
Basics of the Modulo Operator in Python
The % operator is like any other arithmetic operator in Python, and you can use it with int and float numeric types. You can also use the operator in classes you create and with existing methods like math.fmod().
The typical Python developer uses the modulo operator with integers. When the operator is used with two positive integers, it will output the remainder of the Euclidean division. For instance:
>>> 240 % 13 6
It's important to remember that if you use the modulo operator with a divisor 0, Python will throw a ZeroDivisionError. This same error appears if you use the division operator with zero.
>>> 55 % 0 ZeroDivisionError: integer division or modulo by zero
Using the modulo operator with a float value also returns the remainder of division, but the resultant value is a float value. Here's an example:
>>> 12.5 % 5.5 1.5
Besides using the modulo operator directly with float values, you can use the math.fmod() function to apply the modulo operator on any float value.
The following code gives the same result as the operator performed above:
>>> import math >>> math.fmod(12.5, 5.5) 1.5
Interestingly, the official Python documentation recommends using this very function in the math class rather than the modulo operator when working with float values.
This is because the fmod() method calculates the result of the modulo operation differently than when % is used. The difference in the approach is apparent when you use a negative operand with the fmod() method versus the % operator. We discuss negative operands in the next section.
It's important to note that you may encounter precision and rounding issues when using the operator and the fmod() method on floating-point numbers.
Using Modulo with Negative Operands
The examples of the modulo operations you have seen so far involve two positive operands, and all of the operations return predictable results.
However, calculating the remainder with the modulo operation becomes more complicated when a negative operand is introduced.
One interesting quirk is the ambiguity surrounding which sign the remainder should take – that of the dividend or the divisor – when the operand is negative.
On the other hand, Python prefers taking the sign of the divisor. So, the same operation of 8 % -3 gives the result -1.
r = a - (n * trunc(a/n))
Where n is the divisor, a is the dividend, and r is the remainder. The trunc() method in the equation is the truncated division method responsible for rounding off the negative number toward zero.
Let's work out the steps of this equation, taking the values from earlier, and see how it arrives at result "2":
r= 8 - (-3 * trunc(8/-3))
r = 8 - (-3 * trunc(-2.666666666667))
r = 8 - (-3 * -2) # Rounding towards zero
r = 8 - 6
r = 2
In contrast, Python and similar languages use the following equation to work out the remainder:
r = a - (n * floor(a/n))
Here, the floor() method indicates that Python uses floor division, which returns the same results as a truncated division when positive numbers are passed to it.
But when a negative number is passed, the floor division method will round the result away from zero, like so:
r = 8 - (-3 * floor(8/-3))
r = 8 - (-3 * floor(-2.666666666667))
r = 8 - (-3 * -3) # Rounded away from 0
r = 8 - 9
r = -1
You might be wondering how this difference in approach might apply to you if you only use Python. Interestingly, the modulo operations in Python are not all the same.
It is true that when the modulo is used with int and float types, the remainder will take the divisor's sign. However, this won't be the case when the modulo is used with other types.
For instance, performing 8.0 % -3.0 will give you -1.0. But on supplying the same values to the fmod() method, you will see:
>>> import math >>> math.fmod(8.0, -3.0) 2.0
Precedence of Modulo Operator in Python
The modulo operator has the same precedence level as the multiplication and division operators. This means that Python evaluates the modulo operator from left to right with the other operators in the same precedence level.
Let's understand this with an example. Consider the expression 4 * 10 % 12 - 9.
Since the multiplication and modulo operators hold the same precedence, Python evaluates them from left to right. The math is worked out in three steps.
First, 4 * 10 is evaluated, resulting in 40 % 12 - 9. Second, 40 % 12 is evaluated, resulting in 4 - 9. Finally, 4 - 9 is evaluated, resulting in -5.
The nice thing about Python is that you can freely use parentheses to override the precedence of operators. Simply surround the operation you want to evaluate first with parentheses.
So, in the expression 4 * 10 % (12 - 9), (12 - 9) is evaluated first, followed by 4 * 10, and finally, 40 % 3. The result of the expression is 1.
You can write efficient and precise Python code by understanding the modulo operator's precedence and using parentheses to modify it. So, remember these rules when writing Python expressions to ensure your operations give the intended results.