Python Arithmetic Operators: Syntax and Behavior
python arithmetic operators: A practical reference to Python's arithmetic operators, covering true vs floor division, modulo with negatives, precedence, type coercion,...
Python's arithmetic operators look familiar to anyone who has written code in another language, but several of them behave differently than their counterparts in C, Java, or JavaScript. The python arithmetic operators set includes +, -, *, /, //, %, and **, and the differences appear most sharply in division, modulo, and exponentiation.
The Core Operator Set and What Each One Returns
| Operator | Name | Result type (int operands) |
|---|---|---|
+ | Addition | int |
- | Subtraction | int |
* | Multiplication | int |
/ | True division | float |
// | Floor division | int |
% | Modulo | int |
** | Exponentiation | int or float |
The critical distinction is between / and //. In Python 3, / always returns a float, even when both operands are integers. // performs floor division, returning the largest integer less than or equal to the exact quotient.
print(7 / 2) # 3.5 print(7 // 2) # 3
This is a deliberate break from Python 2, where / truncated toward zero for integer operands. Code that relies on truncation behavior will silently produce different results on Python 3.
True Division vs Floor Division
True division returns the mathematically exact quotient as a float. Floor division rounds down toward negative infinity, not toward zero. That distinction matters for negative operands.
print(-7 // 2) # -4 print(int(-7 / 2)) # -3
-7 / 2 is -3.5, and floor division rounds down to -4. If you need truncation toward zero, you must convert explicitly or use int() on the true division result. This is a common source of bugs when porting code from languages where integer division truncates toward zero.
Modulo With Negative Operands
The % operator in Python follows the sign of the divisor, which is different from languages like C or Java where the result takes the sign of the dividend.
print(7 % 3) # 1 print(-7 % 3) # 2 print(7 % -3) # -2
The result satisfies the identity a == (a // b) * b + a % b. For -7 % 3, -7 // 3 is -3, and (-3) * 3 is -9, so -7 % 3 must be 2 to make the identity hold.
This behavior is useful for cyclic indexing, where you want to wrap a negative index into a valid range:
items = ["a", "b", "c"] print(items[-1 % len(items)]) # "c"
Operator Precedence and Associativity
Python's arithmetic operators follow a defined precedence order. From highest to lowest:
**(exponentiation)- unary
+,-(unary operators) *,/,//,%(multiplicative)+,-(additive)
Exponentiation is right-associative, so 2 ** 3 ** 2 evaluates as 2 ** (3 ** 2), which is 2 ** 9 = 512, not (2 ** 3) ** 2 = 64.
print(2 ** 3 ** 2) # 512 print((2 ** 3) ** 2) # 64
The multiplicative operators are left-associative, so 100 / 10 * 2 is (100 / 10) * 2 = 20.0, not 100 / (10 * 2) = 5.0.
A subtle precedence issue involves unary minus and exponentiation. -2 ** 2 evaluates as -(2 ** 2), which is -4, not (-2) ** 2 = 4. This catches many developers who expect the unary minus to bind more tightly.
Type Coercion in Mixed-Type Arithmetic
When operands have different numeric types, Python applies a coercion hierarchy: bool → int → float → complex. The result takes the wider type.
print(3 + 0.5) # 3.5 (float) print(True + 1) # 2 (bool coerces to int) nprint(2 * 3j) # 6j (complex)
Booleans are a subclass of int in Python, so True behaves as 1 in arithmetic. This can produce surprising results if you accidentally use a boolean where an integer is expected.
For division, the coercion rule means that even 4 / 2 returns 2.0, not 2. If you need an integer result, you must explicitly convert or use floor division.
Augmented Assignment Operators
Python provides augmented assignment forms for all arithmetic operators: +=, -=, *=, /=, //=, %=, **=.
total = 10 total += 5 # total is now 15 total //= 2 # total is now 7
These operators evaluate the left-hand side once, which matters when the target is a subscription or attribute:
data = [1, 2, 3] data[0] += 10 # reads data[0], adds 10, writes back
The augmented form is equivalent to data[0] = data[0] + 10, but the expression data[0] is evaluated only once. For simple variable names this makes no difference, but for obj.attr or items[key] it avoids a duplicate lookup.
Performance and Runtime Considerations
Arithmetic on small integers is fast because Python caches integer objects in the range -5 to 256. Operations on integers outside that range allocate new objects, but the arithmetic itself is still a single C-level operation.
Floating-point arithmetic follows IEEE 754 semantics, so you should be aware of precision limits. Adding 0.1 + 0.2 does not produce exactly 0.3 in binary floating point:
print(0.1 + 0.2) # 0.30000000000000004
This is not a Python bug; it is inherent to binary floating-point representation. If exact decimal arithmetic is required, use the decimal module, which is slower but provides exact decimal behavior.
For large integer arithmetic, Python's int type supports arbitrary precision, so multiplication of very large integers works without overflow, but it is slower than native machine-word arithmetic. The performance cost grows with the number of digits.
Common Pitfalls When Using Arithmetic Operators
One frequent mistake is using / when // is intended, producing a float that then causes type errors downstream. Another is assuming modulo follows the dividend's sign. A third is forgetting that ** binds more tightly than unary minus.
# Pitfall 1: unexpected float count = 7 half = count / 2 # 3.5, not 3 # Pitfall 2: negative modulo print(-7 % 3) # 2, not -1 # Pitfall 3: exponentiation with unary minus print(-2 ** 2) # -4, not 4
When you need truncation toward zero for negative numbers, use int(a / b) or math.trunc(a / b). When you need the mathematical modulo that always returns a non-negative result, Python's % already does that when the divisor is positive.
Choosing the Right Operator for the Job
The choice between / and // should be driven by the type you need downstream. If you are computing an index, a count, or a step size, // is usually correct. If you are computing a ratio, a rate, or a measurement, / is correct.
For modulo, use % when you need cyclic wrapping or remainder semantics. When the divisor is positive, Python's modulo always returns a result in [0, divisor), which makes it reliable for indexing and rotation logic.
For exponentiation, ** works for both integer and float exponents. 2 ** 0.5 returns the square root as a float. For modular exponentiation with large numbers, consider pow(base, exp, mod), which avoids constructing the full intermediate result:
result = pow(2, 100, 1000) # 376, computed efficiently
The three-argument form of pow is significantly more efficient than (2 ** 100) % 1000 because it reduces the intermediate value at each multiplication step rather than computing the full power first.