Working with numbers
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:
print(7 / 2)print(7 // 2)print(7 % 2)print(6 / 3)3.5312.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:
print(-7 // 2)print(-7 % 2)print(divmod(17, 5))-41(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:
print(2 ** 10)print(2 ** 100)print(1_000_000 + 1)102412676506002282294014967032053761000001Underscores 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.
print(abs(-7), abs(2.5))print(min(3, 8, 1), max(3, 8, 1))print(max(2.5, 4))7 2.51 84min and max accept any number of arguments. Lesson 19 shows that they also work on whole lists.
print(round(3.14159, 2))print(round(2.5), round(3.5))print(round(2.675, 2))3.142 42.67round() 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
print(0.1 + 0.2)print(0.1 + 0.2 == 0.3)0.30000000000000004FalseMost 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.
import math
print(math.isclose(0.1 + 0.2, 0.3))print(math.sqrt(16))print(math.floor(-2.5), math.ceil(-2.5))True4.0-3 -2math.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.
from decimal import Decimalfrom 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))0.3True1/2Always build a Decimal from a string. If you pass a float, you get the float’s exact binary value, error included:
from decimal import Decimal
print(Decimal(0.1))0.1000000000000000055511151231257827021181583404541015625A 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.
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)Year 1: 1050.00Year 2: 1102.50Year 3: 1157.63The 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.3isFalse. Usemath.isclose(). - Expecting
/to give anint. Use//when you want whole-number division. - Negative floor division.
-7 // 2is-4. If you need truncation toward zero, useint(-7 / 2). Decimal(0.1). This copies the float’s error into the Decimal. WriteDecimal("0.1").- Mixing
Decimalandfloat.Decimal("1.5") + 0.5raises aTypeError. Convert deliberately. - Naming a variable
max,minorround. It hides the built-in. See the variables lesson.
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
total_minutes = 135hours, minutes = divmod(total_minutes, 60)print(hours, "hours", minutes, "minutes")2 hours 15 minutesdivmod(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.
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
first = 3second = -8print(abs(first - second), min(first, second), max(first, second))11 -8 3first - 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.
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
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)Year 1: 1035.00Year 2: 1071.23Year 3: 1108.72Only 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, andintnever overflows.abs,min,max,roundanddivmodhandle everyday number jobs.- Floats are approximate: compare them with
math.isclose(). UseDecimal("...")for money andFractionfor exact ratios.
Next: operators and precedence.