Python Part I · Module 3 · Operators and Expressions
Arithmetic: + - * / // % **
Four of Python's seven arithmetic operators need no introduction — you have been reading plus, minus and times since primary school, and they behave as you expect. This chapter spends almost no time on those and concentrates on the three that genuinely catch people out. Python has two different division operators and they answer different questions, so choosing wrongly gives an answer that is not so much incorrect as about something else. The remainder operator is more useful than it first looks and behaves differently from C when negative numbers are involved. And the power operator is not the symbol most engineers reach for first, which is a mistake that produces a plausible wrong number rather than an error.
Foundation12 min readPythonPart IFundamentalsArithmeticDivision
Part I · Module 3 · Chapter 3.2 · Arithmetic: + - * / // % **
1. The Four You Already Read
total_tests = 120
failed_tests = 7
print(total_tests + failed_tests)
print(total_tests - failed_tests)
print(total_tests * 2)
print(-failed_tests)127
113
240
-7Addition, subtraction, multiplication, and negation. They do what they look like. The last one is worth naming: a single - in front of a value negates it, and that is a different use of the symbol from subtraction.
Nothing here needs explaining, so the chapter moves on.
2. Mixing Whole Numbers and Decimals
One rule about types, which Chapter 3.1 §3 hinted at:
print(3 + 5)
print(3 + 0.5)
print(type(3 + 5))
print(type(3 + 0.5))8
3.5
<class 'int'>
<class 'float'>If either value is a float, the result is a float. Python does not discard the fractional part to keep the answer whole — it widens the result instead.
That is the sensible behaviour: 3 + 0.5 is genuinely 3.5, and an int could not hold it.
3. Two Division Operators
Here is the real content of the chapter.
/ always produces a float
print(10 / 4)
print(10 / 5)
print(type(10 / 5))2.5
2.0
<class 'float'>Chapter 2.6 §3 introduced this. / is ordinary division and it always gives a float — even when the division comes out exactly, which is why 10 / 5 shows 2.0 rather than 2.
Use it when you want the real answer: a rate, an average, a period.
total = 600
passed = 589
print(passed / total)0.9816666666666667// rounds the answer down
print(10 // 4)
print(10 // 5)
print(type(10 // 4))2
2
<class 'int'>// is floor division. It gives you the answer rounded down, and for two int values the result is an int.
Use it when a fraction of the thing makes no sense: how many complete batches of 16 fit into 600 jobs, how many whole seconds are in a duration.
jobs = 600
per_batch = 16
print(jobs // per_batch)37Thirty-seven complete batches. There is a remainder, and §4 is about getting at it.
Rounded down, not truncated
This distinction is where the casual description fails, so it is worth thirty seconds.
print(7 // 2)
print(-7 // 2)3
-4If // simply threw away the decimal part, -7 // 2 would be -3 — because -7 / 2 is -3.5 and dropping the .5 gives -3.
It gives -4, because floor division rounds down, toward the more negative number. For positive values rounding down and dropping the fraction are the same thing, which is why the loose description survives so long. For negatives they are not.
4. The Remainder: %
% gives you what is left over after the division.
print(10 % 4)
print(600 % 16)2
8Ten contains two fours with two left over. Six hundred contains thirty-seven sixteens with eight left over — which pairs with the 600 // 16 from §3 to fully describe the division.
// and % are a matched pair: one tells you how many whole times, the other tells you what remains.
What it is actually for
Three uses worth knowing, all reachable with what you have:
Is a number a multiple of something? The remainder is zero exactly when it divides evenly.
address = 4096
print(address % 4)04096 is a multiple of 4. A non-zero answer would mean it is not — which is how an alignment check works, once Module 9 lets you act on the answer.
Even or odd. The remainder after dividing by two.
print(7 % 2)
print(8 % 2)1
0Wrapping round. A value that must stay within a range.
print(13 % 8)5Counting to 13 in a system that only has positions 0 to 7 lands you at position 5.
Negatives behave differently from C
Kept short, but stated because it is a real difference.
print(-7 % 3)2C would give -1 here. Python's result takes the sign of the right-hand value, so % 3 always produces something from 0 to 2.
That is usually what you want for a wraparound, and it is worth knowing if you are porting arithmetic from C.
5. Powers: **
print(2 ** 8)
print(2 ** 10)
print(10 ** 3)256
1024
1000** raises the left value to the power of the right. Chapter 2.6 noted that Python integers have no practical size limit, and this is where you notice:
print(2 ** 64)18446744073709551616No overflow, no wraparound. The whole number is produced.
6. Two Errors Worth Meeting
Dividing by zero
print(10 / 0)ZeroDivisionError: division by zeroPython stops rather than producing an infinity or a meaningless number. // and % fail the same way against zero.
Arithmetic on text
print("4210" + 1)TypeError: can only concatenate str (not "int") to strThis is Chapter 2.9 §3 arriving as an actual failure. "4210" is text; 1 is a number; + has no meaning across the two.
The message is Python explaining that + on a str means join text together, and it cannot join a number onto it. The fix is Chapter 2.11's conversion:
print(int("4210") + 1)42117. Summary of the Seven
Now that each has been explained, they compress usefully:
| Operator | Question it answers | Example | Result |
|---|---|---|---|
+ | sum | 120 + 7 | 127 |
- | difference, or negation | 120 - 7 | 113 |
* | product | 120 * 2 | 240 |
/ | the real answer | 10 / 4 | 2.5 — always a float |
// | how many whole times, rounded down | 10 // 4 | 2 |
% | what is left over | 10 % 4 | 2 |
** | raised to a power | 2 ** 8 | 256 |
8. Predict, Run, Observe
Predict value and type for each.
print(20 + 4)
print(20 / 4)
print(20 // 4)
print(20 % 4)
print(20 ** 2)
print(20 / 3)
print(20 // 3)
print(20 % 3)
print(-20 // 3)
print(type(20 // 4))
print(type(20 / 4))24
5.0
5
0
400
6.666666666666667
6
2
-7
<class 'int'>
<class 'float'>Two to check: 20 / 4 gave 5.0 not 5, and -20 // 3 gave -7 not -6. If both matched your prediction, §3 landed.
9. Exercise
Exercise 1 — pick the operator. For each, say which of /, // or % you would use: (a) the average runtime across 600 tests; (b) how many complete groups of 8 fit into 100; (c) whether an address divides evenly by 4; (d) the pass rate as a fraction.
Exercise 2 — the matched pair. Given jobs = 1000 and per_batch = 32, predict jobs // per_batch and jobs % per_batch, then run both. Explain in one sentence what the two numbers together tell you.
Exercise 3 — the negative case. Predict all four, then run them:
print(9 // 2)
print(-9 // 2)
print(9 % 2)
print(-9 % 2)Which two would differ in C, and why?
Exercise 4 — the silent typo. Predict both, then run:
print(3 ** 4)
print(3 ^ 4)Neither fails. Explain in one sentence why that makes the mistake worse than an error would be.
Exercise 5 — fix the error. This fails. Predict the error, then correct it so it prints 4211:
print("4210" + 1)10. Summary
+ - *and negation behave as expected, and mixing anintwith afloatproduces afloat./always produces afloat, even when the division is exact.//rounds the result down — not "removes the decimal part".-7 // 2is-4, where C would give-3.%gives the remainder, and pairs with//to describe a division completely. Useful for multiples, even/odd and wraparound.- Python's
%takes the sign of the right operand, so-7 % 3is2rather than C's-1. **raises to a power, with no size limit and no overflow.^is not exponentiation. It is XOR, it produces a wrong number silently, and Chapter 3.7 covers what it actually does.- Dividing by zero raises
ZeroDivisionError; arithmetic between text and a number raisesTypeError.
Next: the shorthand for updating a value in terms of itself, which appears in almost every script you will read.
Where this fits
Part of the Python curriculum.
