7.6 Numeric Operators: //, %, **, divmod()
Floor Division (//)
The floor division operator, //, performs division and then rounds the result down to the nearest whole number, returning an integer. For positive numbers, this is straightforward, as “down” means towards negative infinity. However, its behavior with negative numbers is a common source of confusion.
# With positive numbers
print(10 // 3) # Output: 3 (3.333... rounded down to 3)
print(7 // 2) # Output: 3 (3.5 rounded down to 3)
# With negative numbers
print(-10 // 3) # Output: -4 (-3.333... rounded *down* to -4)
print(10 // -3) # Output: -4 (-3.333... rounded *down* to -4)
print(-10 // -3) # Output: 3 (3.333... rounded down to 3)
The key to understanding this is the mathematical definition of the floor function: it returns the greatest integer less than or equal to the input. For -3.333, the greatest integer less than or equal to it is -4, not -3. This behavior ensures consistency with the modulo operator, as explained in the next section.
Modulo (%)
The modulo operator returns the remainder of a division operation. The result of a % b has the same sign as the divisor b (or is zero). This is a crucial detail that differs from the modulus definition in some other programming languages.
# With positive divisor
print(10 % 3) # Output: 1 (10 = 3*3 + 1)
print(-10 % 3) # Output: 2 (-10 = 3*(-4) + 2)
print(10 % -3) # Output: -2 (10 = -3*(-4) - 2)
print(-10 % -3) # Output: -1 (-10 = -3*3 - 1) - but note, negative divisor is rare and often discouraged.
The relationship between floor division and modulo is defined by the identity: x = (x // y) * y + (x % y). Python’s design ensures this identity always holds, even for negative numbers. This is why -10 // 3 is -4; it must be so that (-4 * 3) + 2 = -10.
Exponentiation (**)
The exponentiation operator raises the left operand to the power of the right operand. It has higher precedence than unary operators on its left, which can lead to unexpected syntax errors.
print(2 ** 3) # Output: 8
print(4 ** 0.5) # Output: 2.0 (square root)
print(9 ** -1) # Output: 0.111... (1/9)
# Precedence pitfall: Unary minus has lower precedence than **
print(-3 ** 2) # Output: -9. It's interpreted as -(3**2), not (-3)**2.
print((-3) ** 2) # Output: 9. Use parentheses for the intended operation.
The divmod() Function
The divmod() function is a convenient tool that returns both the quotient from floor division and the remainder from the modulo operation in a single function call. It takes two arguments, a and b, and returns a tuple (a // b, a % b).
result = divmod(10, 3)
print(result) # Output: (3, 1)
print(result[0]) # Output: 3
print(result[1]) # Output: 1
quotient, remainder = divmod(87, 8) # Tuple unpacking
print(f"Quotient: {quotient}, Remainder: {remainder}") # Output: Quotient: 10, Remainder: 7
# It adheres to the same rules for negative numbers
print(divmod(-10, 3)) # Output: (-4, 2)
Behavior Across Numeric Types
These operators behave differently depending on the numeric types involved.
intandfloat: Mixing these types typically results in afloat.print(5 // 2.0) # Output: 2.0 (float) print(5.5 % 2) # Output: 1.5 (float) print(divmod(5.5, 2)) # Output: (2.0, 1.5)complex: Floor division and modulo are undefined for complex numbers. Attempting to use//,%, ordivmod()will raise aTypeError.Decimal: Thedecimal.Decimaltype supports these operators but is bound by its defined context (precision, rounding mode). Crucially, floor division forDecimalobjects uses the context’s rounding mode (default isROUND_HALF_EVEN), which is different from the always-round-down behavior ofintandfloat.from decimal import Decimal, getcontext print(Decimal('10') // Decimal('3')) # Output: 3 getcontext().prec = 3 getcontext().rounding = 'ROUND_UP' print(Decimal('10') // Decimal('3')) # Output: 4Fraction: Thefractions.Fractiontype supports these operators and returns results that are alsoFractionobjects, preserving exact arithmetic.from fractions import Fraction f = Fraction(10, 3) print(f // 2) # Output: 1 (Fraction(5, 3) is ~1.66, floored to 1/1) print(divmod(Fraction(10, 3), Fraction(1, 2))) # Output: (Fraction(6, 1), Fraction(2, 3))
Best Practices and Common Pitfalls
- Clarity with Negatives: Be extremely cautious when using
//and%with negative numbers. If your problem domain involves negative values, explicitly document the intended behavior or use conditional logic to handle cases where the sign of the divisor or dividend matters. - Prefer
divmod()for Paired Operations: When you need both the quotient and remainder, usingdivmod()is not only more concise but can also be more efficient than calling the two operators separately, as it may calculate both results in a single internal operation. - Type Awareness: Remember that operations involving a
floatwill return afloat, which might not be desired in contexts requiring integer results. Be explicit with type conversion if necessary (int(x // y)). - Undefined for Complex Numbers: A common error is attempting to check the “remainder” of a complex number division. This is mathematically nonsensical. Use the absolute value (
abs()) or other complex number properties instead.