Numbers and Arithmetic
Arithmetic, floor division, modulo, exponentiation, and operator precedence.
What you will be able to do
- Use the seven arithmetic operators and predict the type each one returns
- Explain the difference between /, //, and %, including on negative numbers
- Apply operator precedence, and use parentheses where it matters
- Use abs, round, pow, divmod, min, max, and sum
- Say why round(2.5) is 2 and not 3
- Write the calculations applications actually need: percentages, discounts, tax, averages, pagination
The idea, in plain English
Almost every program counts something. Prices and quantities, marks and averages, lessons completed out of a total, users this week against users last week - the arithmetic itself is school-level, and the part that catches people out is which operator to reach for and what type comes back.
Python has seven arithmetic operators. Four behave exactly as you would expect. The three that do not - `/`, `//`, and `%` - are worth the time this lesson spends on them, because between them they cause most beginner arithmetic bugs.
The first surprise is that `/` always gives you a float. `10 / 2` is 5.0, not 5, even though the answer is a whole number. If you want an integer back, `//` is the operator you meant.
The second is that arithmetic is not evaluated left to right. `10 + 5 * 2` is 20, because multiplication binds tighter than addition. Python follows ordinary mathematical precedence, which is convenient right up until you assume it does not.
Worked example: Turning 7384 seconds into 2 hours, 3 minutes, 4 seconds.
The three ways to divide
Given 10 and 3, Python can answer three different questions, and they are genuinely different questions rather than three formats of one answer.
The one to be careful with is `//` on negative numbers. It does not chop off the decimal part - it rounds down, towards negative infinity. So -10 // 3 is -4, not -3, and that is a different result from int(-10 / 3), which truncates towards zero and gives -3.
/10 / 3 is 3.3333333333333335. True division. Always returns a float, even when the result is whole - 10 / 2 is 5.0.//10 // 3 is 3. Floor division - how many whole 3s fit. Returns an int when both operands are ints.%10 % 3 is 1. The remainder left over. Nothing to do with percentages.divmod()divmod(10, 3) is (3, 1). Both answers at once, which is exactly what time and pagination need.// on negatives-10 // 3 is -4, because floor rounds down rather than towards zero. int(-10 / 3) gives -3 instead.% on negatives-10 % 3 is 2, not -1. The result takes the sign of the divisor, which keeps a % b always in range for indexing.Tip: A good habit: use // when the answer should be a whole number of things - pages, rows, complete groups. Use / when you want a measurement, like an average or a rate.
Why round(2.5) is 2
round(2.5) gives 2, and round(3.5) gives 4. That is not a bug and it is not inconsistent - Python rounds a half to the nearest even number, which is often called banker’s rounding.
The reason is bias. Always rounding halves up makes a long column of numbers drift upward; rounding to even sends half of them up and half down, so the errors cancel over many values. Statistics and accounting both care about that.
It surprises people mostly when they test with a single number. If you genuinely need "always round halves up" - and invoices sometimes specify it - use Decimal with an explicit rounding mode rather than fighting round().
Watch out: round() on a float can also surprise for a second reason: round(2.675, 2) is 2.67, because 2.675 is not stored as exactly 2.675. Decimal avoids both problems at once.
Precedence, in the order Python applies it
Parentheses first, then exponentiation, then unary minus, then the multiplying group, then the adding group. Within a group, evaluation runs left to right - except `**`, which runs right to left, so 2 ** 3 ** 2 is 2 ** 9 and not 8 ** 2.
You do not need to memorise this. You need to know it exists, and to add parentheses the moment an expression stops being obvious at a glance - which is usually the second operator.
( )Parentheses. Always evaluated first, and the only tool you need to override everything below.**Exponentiation. Binds right to left: 2 ** 3 ** 2 is 512, not 64.-xUnary minus. Note that -2 ** 2 is -4, because ** binds tighter than the minus sign.* / // %Multiplication, division, floor division, remainder. Left to right among themselves.+ -Addition and subtraction. Last, which is why 10 + 5 * 2 is 20.Syntax and examples
a, b = 10, 3
print(a + b) # 13
print(a - b) # 7
print(a * b) # 30
print(a / b) # 3.3333333333333335 <- float, always
print(a // b) # 3 <- int, floored
print(a % b) # 1 <- remainder
print(a ** b) # 1000print(10 // 3) # 3
print(-10 // 3) # -4 floors towards negative infinity
print(int(-10 / 3)) # -3 truncates towards zero
print(10 % 3) # 1
print(-10 % 3) # 2 takes the sign of the divisorprint(10 + 5 * 2) # 20 not 30
print((10 + 5) * 2) # 30
print(10 + 5 * 2 ** 2) # 30 ** first: 5 * 4 = 20, then 10 + 20
print(2 ** 3 ** 2) # 512 ** is right to left: 2 ** 9
print(-2 ** 2) # -4 ** binds tighter than unary minus
print((-2) ** 2) # 4print(abs(-250)) # 250
print(round(10.6789, 2)) # 10.68
print(pow(2, 10)) # 1024
print(pow(2, 10, 1000)) # 24 (2**10) % 1000, done efficiently
print(divmod(10, 3)) # (3, 1)
marks = [85, 92, 78, 90, 88]
print(min(marks), max(marks), sum(marks)) # 78 92 433
print(sum(marks) / len(marks)) # 86.6total_seconds = 7384
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
print(f"{hours}h {minutes}m {seconds}s") # 2h 3m 4s# Percentage complete
completed, total = 24, 80
print(completed / total * 100) # 30.0
# Discount
price, discount_pct = 2000, 10
final = price * (1 - discount_pct / 100)
print(final) # 1800.0
# Tax on top
subtotal, tax_pct = 1000, 18
print(subtotal * (1 + tax_pct / 100)) # 1180.0
# Pagination - // and % answer both halves
total_items, per_page = 127, 10
print(total_items // per_page, total_items % per_page) # 12 7
print(-(-total_items // per_page)) # 13 pages neededTip: To round a division up rather than down, -(-a // b) is the standard trick - two negations turn a floor into a ceiling without importing math. It is how you work out how many pages 127 items need.
Arithmetic operators
+Addition. Also joins strings and lists, which is why "1" + 1 is an error rather than 2.
10 + 3 # 13
-Subtraction.
10 - 3 # 7
*Multiplication. On a string it repeats: "ab" * 3 is "ababab".
10 * 3 # 30
/True division. Always a float, even for exact results.
10 / 3 # 3.333...
//Floor division. Rounds down, towards negative infinity.
10 // 3 # 3
%Remainder. The workhorse behind even/odd, cycling, and pagination.
10 % 3 # 1
**Exponentiation. Right-associative, and accepts negative and fractional exponents.
10 ** 3 # 1000
Numeric built-in functions
abs(x)Distance from zero, sign removed. Useful for "how far apart are these".
abs(-250) # 250
round(x, n)Rounds to n decimal places, or to a whole number when n is omitted. Halves go to even.
round(10.6789, 2) # 10.68
pow(x, y, m)x to the power y. With a third argument, does it modulo m efficiently - used in cryptography.
pow(2, 10, 1000) # 24
divmod(x, y)Quotient and remainder together, as a tuple. Unpacks straight into two names.
h, m = divmod(185, 60)
min() / max()Smallest and largest. Accept either several arguments or one iterable.
min(10, 5, 20) # 5
sum()Adds an iterable of numbers. Pair with len() for an average.
sum(marks) / len(marks)
Try it yourself
The code does not change. Swap the content string and the program does something else entirely.
“print(-10 // 3, int(-10 / 3))”
“print(round(0.5), round(1.5), round(2.5), round(3.5))”
“print(2 ** 3 ** 2, (2 ** 3) ** 2)”
“print(-(-127 // 10))”
What usually goes wrong
/ always returns a float. Using it for a count gives you 12.0 pages or 5.0 items, which then prints with a decimal point and fails an equality check against an int.
✗ pages = total_items / per_page # 12.7✓ pages = total_items // per_page # 12It is the remainder operator. 10 % 3 is 1, not 333%. A percentage is ordinary division and multiplication: part / whole * 100.
✗ percentage = completed % total✓ percentage = completed / total * 10010 + 5 * 2 is 20. Multiplication, division, floor division, and remainder all bind tighter than addition and subtraction, and ** binds tighter still.
✗ total = price + price * tax_rate / 100 # works, but only by luck✓ total = price + (price * tax_rate / 100) # says what you meantCovered in the data types lesson and worth repeating here, because this is the lesson where you start writing totals. Binary floats cannot hold 0.1 exactly, so a bill can come out a paisa short.
✗ total = 19.99 + 1.50✓ from decimal import Decimal
total = Decimal("19.99") + Decimal("1.50")round(2.5) is 2. Python rounds halves to the nearest even value to avoid upward bias across many numbers. Test with more than one value before concluding it is broken.
✗ round(2.5) # expected 3, got 2✓ from decimal import Decimal, ROUND_HALF_UP
Decimal("2.5").quantize(Decimal("1"), ROUND_HALF_UP)Best practices
- Choose // when the result counts things and / when it measures something.
- Add parentheses as soon as an expression has two different operators, even where precedence already agrees with you.
- Use divmod() when you need both the quotient and the remainder - it says what you mean and computes once.
- Keep money in Decimal or in the smallest integer unit. Never in float.
- Round only at the point of display, never during a calculation, so errors cannot accumulate.
- Use sum(xs) / len(xs) for an average, and guard against len(xs) being zero.
Practice
Write these yourself before opening anything. Getting them wrong first is most of how this sticks.
With a = 25 and b = 7, print the result of all seven arithmetic operators.
Show solutionHide solution
a, b = 25, 7
print(a + b) # 32
print(a - b) # 18
print(a * b) # 175
print(a / b) # 3.5714285714285716
print(a // b) # 3
print(a % b) # 4
print(a ** b) # 6103515625Given number = 27, use % to print "Even" or "Odd".
Show hintHide hint
A number is even when the remainder after dividing by 2 is 0.
Show solutionHide solution
number = 27
if number % 2 == 0:
print("Even")
else:
print("Odd") # OddFrom marks = [85, 90, 76, 88, 92], print the total, the number of subjects, the average, the highest and the lowest.
Show solutionHide solution
marks = [85, 90, 76, 88, 92]
print("Total: ", sum(marks)) # 431
print("Count: ", len(marks)) # 5
print("Average:", sum(marks) / len(marks)) # 86.2
print("Highest:", max(marks)) # 92
print("Lowest: ", min(marks)) # 76A 2500 item has a 15% discount. Print the discount amount and the final price.
Show solutionHide solution
price = 2500
discount_percentage = 15
discount = price * discount_percentage / 100
final_price = price - discount
print("Discount: ", discount) # 375.0
print("Final price:", final_price) # 2125.0A student has completed 32 of 80 lessons. Print the completion percentage with a % sign after it.
Show hintHide hint
An f-string will place the number inside the text for you.
Show solutionHide solution
completed = 32
total = 80
progress = completed / total * 100
print(f"{progress}%") # 40.0%Convert 367 minutes into hours and minutes using divmod().
Show solutionHide solution
total_minutes = 367
hours, minutes = divmod(total_minutes, 60)
print(f"{hours} hours {minutes} minutes") # 6 hours 7 minutesOf 1200 students, 840 have finished. Print the completion percentage, how many remain, and the ratio of completed to remaining.
Show hintHide hint
The ratio is just one divided by the other.
Show solutionHide solution
total_students = 1200
completed_students = 840
remaining = total_students - completed_students
print(f"Completed: {completed_students / total_students * 100}%") # 70.0%
print(f"Remaining: {remaining}") # 360
print(f"Ratio: {completed_students / remaining:.2f} to 1") # 2.33 to 1Shopping bill calculator
Work a price and quantity through a discount and then tax, printing each stage. The order matters: discount comes off first, tax goes on what is left.
- Start from a product price, a quantity, a discount percentage, and a tax percentage
- Print the subtotal, the discount amount, the price after discount, the tax amount, and the final bill
- Apply tax to the discounted price, not to the subtotal
- Round every money value to two decimal places for display only
- Lay the output out so the numbers line up and can be read as a bill
product_price = 2500
quantity = 3
discount_percentage = 10
tax_percentage = 18
# subtotal -> discount -> after discount -> tax -> finalShow one solutionHide solution
product_price = 2500
quantity = 3
discount_percentage = 10
tax_percentage = 18
subtotal = product_price * quantity
discount = subtotal * discount_percentage / 100
after_discount = subtotal - discount
# Tax applies to what is actually being paid, so it comes after the discount.
tax = after_discount * tax_percentage / 100
final_bill = after_discount + tax
print(f"Product price {product_price:>12,.2f}")
print(f"Quantity {quantity:>12}")
print(f"{'-' * 28}")
print(f"Subtotal {subtotal:>12,.2f}")
print(f"Discount {discount_percentage}% {-discount:>12,.2f}")
print(f"After discount {after_discount:>12,.2f}")
print(f"Tax {tax_percentage}% {tax:>12,.2f}")
print(f"{'-' * 28}")
print(f"Final bill {final_bill:>12,.2f}")
# Subtotal 7,500.00
# Discount 10% -750.00
# After discount 6,750.00
# Tax 18% 1,215.00
# Final bill 7,965.00Key points
- / is true division and always returns a float, even when the answer is whole.
- // is floor division: it rounds down towards negative infinity, so -10 // 3 is -4.
- % is the remainder, not a percentage. -10 % 3 is 2, taking the sign of the divisor.
- divmod(a, b) gives the quotient and remainder together, and unpacks into two names.
- ** is exponentiation, binds right to left, and binds tighter than unary minus - so -2 ** 2 is -4.
- Precedence runs: parentheses, **, unary minus, then * / // %, then + -.
- round() rounds halves to the nearest even number, so round(2.5) is 2.
- abs, round, pow, divmod, min, max, and sum cover nearly all everyday numeric work.
- An average is sum(xs) / len(xs), and len(xs) being zero will raise.
- Use Decimal for money, and round only when displaying.
Quick check before you move on
Interview questions
What is the difference between // and int() on a negative number?
// floors - it rounds towards negative infinity - so -10 // 3 is -4. int() truncates towards zero, so int(-10 / 3) is -3. They agree on positive numbers and diverge on negative ones, which is exactly the kind of difference that survives testing and then fails in production.
Why is -10 % 3 equal to 2 rather than -1?
Python defines the remainder to take the sign of the divisor, so a % b is always in [0, b) for positive b. That makes it safe for indexing and cycling - i % len(xs) is always a valid index. C and Java take the sign of the dividend instead, which is why the same expression gives -1 there.
Why does / return a float even for 10 / 2?
Consistency of type. If / returned int sometimes and float others, the return type of an expression would depend on its runtime values, and every caller would have to handle both. Python 3 made / always true division and gave the integer behaviour its own operator, //.
What is the third argument to pow() for?
Modular exponentiation - pow(base, exp, mod) computes (base ** exp) % mod without ever building the enormous intermediate number. It is what makes RSA and Diffie-Hellman practical, where the exponents have hundreds of digits.
What is floating-point precision, in one sentence?
Floats are stored in binary, and most decimal fractions have no exact binary representation, so values like 0.1 are held as very close approximations and the tiny errors become visible when you combine them.
Why is Decimal preferred over float for money?
Decimal stores base-10 digits exactly, so 0.1 + 0.2 really is 0.3 and a total matches the figure on the invoice. It also lets you set precision and a rounding mode explicitly, which accounting rules usually specify. The cost is speed, which almost never matters in a billing path.
Python integers have no maximum. What does that cost?
Arbitrary precision means no overflow, which removes a whole class of bug. The price is that an int is a heap object rather than a machine word, so arithmetic is slower and memory larger than a fixed-width type - which is why NumPy offers int32 and int64 for numeric work at scale.
How would you calculate a percentage safely?
part / whole * 100, with a guard for whole being zero, since that raises ZeroDivisionError rather than returning infinity. Multiply after dividing rather than before when the numbers are large, and round only for display.
Quiz
- 1.
What is the difference between / and //?
- 2.
What does % return, and what is it commonly used for?
- 3.
What does divmod(10, 3) return?
- 4.
What is 2 ** 3 ** 2, and why?
- 5.
Why does round(2.5) return 2?
- 6.
How do you round a division up rather than down?
Comments
Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.
Loading comments...
AI
System Design
Backend
- GraphQL8 modules · 69 lessons planned
- Core Python13 modules · 75 lessons planned
- FastAPI5 sections · 20 lessons
- Node.js14 modules · 206 lessons planned
- Node.js Performance7 chapters · 36 topics
- Event Loop Lifecycle6 phases · 3 scenarios
- Docker & Containerization11 modules · 144 lessons planned
- AWS for Developers14 modules · 219 lessons planned
- CI/CD & DevOps Automation10 modules · 134 lessons planned