Two things surprise nearly everyone the first time: floats can't store most decimals exactly, and round() doesn't round halves the way you were taught at school.
The surprise
print(0.1 + 0.2) print(0.1 + 0.2 == 0.3) print(round(2.5), round(3.5), round(0.125, 2))
0.30000000000000004 False 2 4 0.12
Why it works
A float is stored in binary, and most decimal fractions (0.1 among them) have no exact binary form, in the same way 1/3 has no exact decimal form. The tiny errors are usually invisible, until you add two of them and compare with ==.
round() uses round half to even (banker's rounding): an exact half goes to the nearest even number. It's the IEEE 754 default because always rounding halves up adds a small upward bias to every total. So 2.5 goes to 2, 3.5 to 4.
The fix
Compare floats with a tolerance, and use Decimal for money, built from strings:
import math from decimal import Decimal, ROUND_HALF_UP print(math.isclose(0.1 + 0.2, 0.3)) price = Decimal("19.99") print(price * 3) print(Decimal("2.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP))
True 59.97 3
Decimal("19.99") is exactly 19.99, so three of them is exactly 59.97. And when a rule really does say "halves go up", quantize with ROUND_HALF_UP says so explicitly.
When not to use it
Don't reach for Decimal in science or data work: floats are much faster, NumPy and pandas are built on them, and the errors are far smaller than any measurement you'll feed in. And never write Decimal(0.1) from a float; it faithfully copies the float's error:
from decimal import Decimal print(Decimal(0.1) == Decimal("0.1"))
False