Exact money with Decimal
Floats are binary fractions, so `0.1 + 0.2` isn't exactly `0.3`. Decimal does decimal arithmetic exactly, and `quantize` rounds to cents with the rule you choose.
- Explain why floats drift with money and why
Decimal("0.1")must be built from text - Round a Decimal to cents with
quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) - Know that the default rounding is half-to-even, and keep floats and Decimals apart
Floats store numbers in binary, and most decimal fractions (0.1, 0.2, 0.07) have no exact binary form. The tiny errors are invisible in a single value and show up when you add, compare or round money: a total that should be 0.30 is 0.30000000000000004, and a tax that should round up rounds down.
from decimal import Decimal gives exact decimal numbers. Build them from text: Decimal("0.1") is exactly one tenth, while Decimal(0.1) faithfully copies the float's binary error. Arithmetic between Decimals stays exact.
Run each example to see what it prints. Running examples doesn't count toward progress.
from decimal import Decimal print(0.1 + 0.2) print(Decimal("0.1") + Decimal("0.2")) print(Decimal(0.1))
Multiplying creates more decimal places: a 7% tax on 6.49 is 0.4543. amount.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) rounds to cents. Name the rule: without it, Decimal rounds half to even (banker's rounding), so 0.125 becomes 0.12, while most receipts expect 0.13.
from decimal import Decimal, ROUND_HALF_UP def with_tax(subtotal_text, rate_text): subtotal = Decimal(subtotal_text) tax = (subtotal * Decimal(rate_text)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) return subtotal + tax print(with_tax("6.49", "0.07")) print(with_tax("66.49", "0.07")) print(Decimal("0.125").quantize(Decimal("0.01")), Decimal("0.125").quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
from decimal import Decimal price = Decimal("19.99") print(price + 0.01)
When to use it: prices, tax, invoices, ledgers: anywhere two totals must match to the cent. Floats are fine for measurements (temperatures, distances). Mistakes to avoid: building Decimals from floats, rounding without naming the rule, and rounding each line differently from the total. str(amount) gives text like 43.77 for reports and APIs.
- Floats are binary approximations; money needs exact decimals.
- Build Decimals from text:
Decimal("19.99"). Don't mix them with floats. - Round with
quantize(Decimal("0.01"), rounding=...); the default is half to even, receipts usually want half up. - Next: finding and checking patterns in text with regular expressions.