Working with numbers

Beginner30 min

Use Python's int and float types, the arithmetic operators, abs, min, max and round, and Decimal and Fraction when you need exact results.

What you will learn
  • Use the division, floor division, modulo and power operators correctly
  • Use abs, min, max, round and divmod
  • Explain why 0.1 + 0.2 is not 0.3 and compare floats safely
  • Choose between float, Decimal and Fraction for a calculation
On this page

Python is a good calculator, and it has two surprises for people coming from other languages: integers never overflow, and the / operator always gives a float. This lesson covers both, plus the standard-library types you reach for when ordinary floats are not accurate enough.

The fundamentals lesson on expressions explains why computers cannot store most decimal fractions exactly. Here we look at what that means in Python.

int and float

An int is a whole number of any size. Python grows it as needed, so there is no overflow to worry about. A float is a 64-bit floating-point number, which is fast but approximate.

Division comes in three flavours:

division.py
print(7 / 2)
print(7 // 2)
print(7 % 2)
print(6 / 3)
Output
3.5
3
1
2.0

/ is true division and always returns a float, even for 6 / 3, which gives 2.0. // is floor division, which drops the fraction. % is the remainder.

With negative numbers, floor division rounds toward negative infinity, not toward zero:

negative_division.py
print(-7 // 2)
print(-7 % 2)
print(divmod(17, 5))
Output
-4
1
(3, 2)

-7 // 2 is -4, not -3, and % is the matching remainder. The two always satisfy a == (a // b) * b + a % b. divmod(a, b) returns both the quotient and the remainder at once, as a pair.

** is the power operator, and because int has no size limit, big results are exact:

powers.py
print(2 ** 10)
print(2 ** 100)
print(1_000_000 + 1)
Output
1024
1267650600228229401496703205376
1000001

Underscores in a number literal, as in 1_000_000, are ignored. They are only there to help you read long numbers.

abs, min, max and round

Four built-in functions cover most everyday number jobs. abs removes a minus sign, min and max pick the smallest and largest of their arguments, and round rounds to the nearest whole number or to a given number of decimal places.

abs_min_max.py
print(abs(-7), abs(2.5))
print(min(3, 8, 1), max(3, 8, 1))
print(max(2.5, 4))
Output
7 2.5
1 8
4

min and max accept any number of arguments. Lesson 19 shows that they also work on whole lists.

rounding.py
print(round(3.14159, 2))
print(round(2.5), round(3.5))
print(round(2.675, 2))
Output
3.14
2 4
2.67

round() rounds halves to the nearest even number, so round(2.5) is 2. And round(2.675, 2) gives 2.67 because 2.675 is stored as a slightly smaller binary value. Both results are correct; they just are not what a school textbook would say.

Why floats surprise you

float_error.py
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
Output
0.30000000000000004
False

Most decimal fractions have no exact binary form, so tiny errors creep in. The fix is not to compare floats with ==.

The math module

Functions such as square roots live in the math module. A module is a file of ready-made tools. The statement import math makes the module available, and you reach its tools with a dot: math.sqrt. Modules and imports covers import in full.

Use math.isclose() instead of == to compare floats.

math_module.py
import math
print(math.isclose(0.1 + 0.2, 0.3))
print(math.sqrt(16))
print(math.floor(-2.5), math.ceil(-2.5))
Output
True
4.0
-3 -2

math.isclose() treats two floats as equal when they are within a small relative tolerance. math.floor() and math.ceil() round down and up, and math.sqrt() always returns a float.

Exact arithmetic: Decimal and Fraction

When you add up money, floats are the wrong tool. The decimal module stores base-10 digits exactly, and fractions.Fraction stores a ratio of two integers exactly. Here the form is from module import name, which brings in just the tool you ask for, so you can write Decimal without the decimal. prefix.

exact_numbers.py
from decimal import Decimal
from fractions import Fraction
print(Decimal("0.1") + Decimal("0.2"))
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))
print(Fraction(1, 3) + Fraction(1, 6))
Output
0.3
True
1/2

Always build a Decimal from a string. If you pass a float, you get the float’s exact binary value, error included:

decimal_from_float.py
from decimal import Decimal
print(Decimal(0.1))
Output
0.1000000000000000055511151231257827021181583404541015625

A calculation that rounds to the cent looks like this. quantize() rounds to the same number of decimal places as its argument, and ROUND_HALF_UP gives the rounding most people expect from money.

compound_interest.py
from decimal import Decimal, ROUND_HALF_UP
balance = Decimal("1000.00")
rate = Decimal("0.05")
cent = Decimal("0.01")
balance = (balance * (1 + rate)).quantize(cent, rounding=ROUND_HALF_UP)
print("Year 1:", balance)
balance = (balance * (1 + rate)).quantize(cent, rounding=ROUND_HALF_UP)
print("Year 2:", balance)
balance = (balance * (1 + rate)).quantize(cent, rounding=ROUND_HALF_UP)
print("Year 3:", balance)
Output
Year 1: 1050.00
Year 2: 1102.50
Year 3: 1157.63

The same two lines repeat for every year. Lesson 16 introduces loops, which remove exactly this kind of repetition.

Common pitfalls

  • Comparing floats with ==. 0.1 + 0.2 == 0.3 is False. Use math.isclose().
  • Expecting / to give an int. Use // when you want whole-number division.
  • Negative floor division. -7 // 2 is -4. If you need truncation toward zero, use int(-7 / 2).
  • Decimal(0.1). This copies the float’s error into the Decimal. Write Decimal("0.1").
  • Mixing Decimal and float. Decimal("1.5") + 0.5 raises a TypeError. Convert deliberately.
  • Naming a variable max, min or round. It hides the built-in. See the variables lesson.
ExerciseMinutes to hours

A film lasts 135 minutes. Print how many whole hours that is and how many minutes are left over, using divmod or // and %.

Input: none. Expected output: 2 hours 15 minutes.

Show solution
minutes_to_hours.py
total_minutes = 135
hours, minutes = divmod(total_minutes, 60)
print(hours, "hours", minutes, "minutes")
Output
2 hours 15 minutes

divmod(135, 60) returns the pair (2, 15), and the pair is unpacked into two names, just like the swap in the variables lesson. Writing total_minutes // 60 and total_minutes % 60 on two lines gives the same result.

ExerciseGap between two readings

Two temperature readings are 3 and -8. Print the size of the gap between them, the lower reading and the higher reading, on one line, using abs, min and max.

Input: none. Expected output: 11 -8 3.

Show solution
reading_gap.py
first = 3
second = -8
print(abs(first - second), min(first, second), max(first, second))
Output
11 -8 3

first - second is 11, so abs has nothing to remove here. If you swap the two readings it would be -11, and abs makes the gap the same either way.

ExerciseCompound-interest calculator

Start with a balance of Decimal("1000.00") and a yearly rate of 3.5%. Print the balance after each of three years, rounded to the cent at every step, in the same format as compound_interest.py. Predict the first line before you run it.

Input: none. Expected output: three lines, the first of which is Year 1: 1035.00.

Show solution
interest_3_5.py
from decimal import Decimal, ROUND_HALF_UP
balance = Decimal("1000.00")
rate = Decimal("0.035")
cent = Decimal("0.01")
balance = (balance * (1 + rate)).quantize(cent, rounding=ROUND_HALF_UP)
print("Year 1:", balance)
balance = (balance * (1 + rate)).quantize(cent, rounding=ROUND_HALF_UP)
print("Year 2:", balance)
balance = (balance * (1 + rate)).quantize(cent, rounding=ROUND_HALF_UP)
print("Year 3:", balance)
Output
Year 1: 1035.00
Year 2: 1071.23
Year 3: 1108.72

Only the rate line changed. Every value is a Decimal built from a string, so no binary rounding error creeps in, and quantize() fixes the result at two decimal places each year.

Summary

  • / gives a float, // and % work on whole numbers, ** is power, and int never overflows.
  • abs, min, max, round and divmod handle everyday number jobs.
  • Floats are approximate: compare them with math.isclose(). Use Decimal("...") for money and Fraction for exact ratios.

Next: operators and precedence.