decimal: Context.prec Counts Significant Digits, Not Decimal Places (tested)
decimal.Context.prec sounds like it might mean "how many digits after the decimal point" -- a natural guess if you're reaching for Decimal to do exact currency math and want 2 digits for cents. We measured what prec actually controls, and what happens to a running total computed under a context set with that assumption.
1. prec=2 does not mean "2 decimal places" -- it means 2 significant digits total
from decimal import Decimal, localcontext
with localcontext() as ctx:
ctx.prec = 2
print(Decimal("19.99") + Decimal("0"))
print(Decimal("123.456") + Decimal("0"))
20
1.2E+2
Measured: with prec=2, adding zero to 19.99 rounds it to 20 -- two significant digits, zero decimal places. 123.456 rounds to 1.2E+2. Every arithmetic operation rounds its result to prec significant digits, regardless of where the decimal point falls -- it has nothing to do with "decimal places" despite the name suggesting otherwise to a currency use case.
2. A running total under that context silently loses real money
from decimal import Decimal, localcontext
with localcontext() as ctx:
ctx.prec = 2
total = Decimal("0")
for _ in range(7):
total = total + Decimal("19.99")
print("running total, prec=2:", total)
exact = sum([Decimal("19.99")] * 7)
print("exact sum, default prec:", exact)
running total, prec=2: 1.4E+2
exact sum, default prec: 139.93
Measured: summing seven 19.99 line items one at a time under prec=2 gives 1.4E+2 (140), seven cents off from the exact 139.93 -- and the error compounds because every intermediate addition re-rounds the running total to 2 significant digits before the next item is added, not just the final display. The construction of each Decimal('19.99') stays exact; it's the arithmetic that rounds, silently, on every single operation.
3. The fix: leave prec at a safe default, use quantize() for display
from decimal import Decimal, ROUND_HALF_UP
total = sum([Decimal("19.99")] * 7)
print("exact total:", total)
print("rounded to cents for display:", total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
exact total: 139.93
rounded to cents for display: 139.93
Measured: computing the sum under the default context (prec=28, far more than any realistic running total needs) and only calling .quantize(Decimal('0.01')) at the point of display or storage gives the exact, correct result. prec is a global ceiling on significant digits for every operation in a calculation chain; decimal-place rounding is a separate, explicit, per-value operation via quantize(). Conflating the two turns a tool chosen specifically for exact currency math into one that silently drops money.
Frequently asked questions
Does decimal.Context.prec control the number of decimal places?
No -- we measured prec controlling the total number of significant digits in the result of every arithmetic operation, regardless of where the decimal point falls. Decimal('123.456') under prec=2 rounds to 1.2E+2, not to 2 digits after the decimal point.
Why did my Decimal running total come out wrong even though I used Decimal instead of float?
If Context.prec was set low (for example, assuming it meant decimal places for currency), every addition in the loop rounds the running total to that many significant digits before the next value is added -- we measured summing seven $19.99 values under prec=2 giving 140 instead of the exact 139.93, with the error compounding across iterations.
What's the right way to round a Decimal to 2 decimal places for currency?
Use .quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) on the final value, not Context.prec. We confirmed this gives the exact, correctly-rounded result without affecting the precision of the arithmetic that produced it.
Does lowering Context.prec affect Decimal values that are just constructed, not computed?
No -- we measured that constructing a Decimal from a string stays exact regardless of the active context's prec. Only the result of an arithmetic operation (+, -, *, /, etc.) gets rounded to prec significant digits.
Is Python's decimal module safe for financial math by default?
Yes, with the default context (prec=28) it has far more headroom than any realistic running total needs, and we measured the default producing the exact correct sum. The risk we measured is specifically from applications setting a low custom prec under the mistaken belief that it limits decimal places rather than total significant digits.
Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in decimal module: set a localcontext() precision of 2, summed seven Decimal('19.99') values in a running total inside that context, and compared the result against the exact sum under the default context and against the correct fix using quantize(). PlainHeaders is our extension; Python's decimal module is built in.