Short Answer
This reference explains how to round to significant figures in Python using the decimal module. It is part of a precision-and-rounding reference site, not merely a calculator. The goal is to give students, engineers, scientists, and educators a reliable, standards-aware guide to significant figures, rounding rules, and the exact behavior of Python’s decimal type.
Rule Statement
Rounding to n significant figures means keeping the first n meaningful digits, starting from the first nonzero digit, and replacing the rest with zeros or dropping them. In Python, the decimal module does not provide a direct round_sig() function. Instead, use Decimal.quantize() with an exponent that corresponds to the desired place value.
For a nonzero Decimal x, the target exponent is:
exponent = x.adjusted() – n + 1
Here adjusted() returns the exponent of the most significant digit. For example, Decimal('1234.567').adjusted() is 3, and Decimal('0.0012345').adjusted() is -3. Then quantize(Decimal('1e{exponent}')) rounds to that place value.
from decimal import Decimal, localcontext, ROUND_HALF_EVEN
def round_sig(x, n, rounding=ROUND_HALF_EVEN):
x = Decimal(x) if not isinstance(x, Decimal) else x
if x.is_zero():
return x
exp = x.adjusted() - n + 1
q = Decimal('1e{}'.format(exp))
with localcontext() as ctx:
ctx.prec = max(28, len(x.as_tuple().digits) + abs(exp) + 5)
return x.quantize(q, rounding=rounding)
Always construct Decimal values from strings, such as Decimal('2.675'), not from binary floats. This avoids hidden binary artifacts that corrupt rounding.
Software Behavior Note
The Python decimal module implements the IBM General Decimal Arithmetic Specification. Its default context rounding mode is ROUND_HALF_EVEN, also called banker’s rounding. This aligns with several metrology and standards conventions for tie-breaking.
quantize() rounds to a fixed exponent, not directly to significant figures. It also respects the current context precision. If the result would have more digits than the context allows, quantize() may raise InvalidOperation. Set ctx.prec high enough for large coefficients, especially when rounding very large or very small numbers.
Zero is special: Decimal('0').adjusted() is 0, but the number of significant figures in zero is ambiguous. Standards such as ambiguous trailing zeros treat zeros as significant only when explicitly indicated. The helper above returns zero unchanged.
Worked Examples
Example 1: 1234.567 to 3 significant figures
Decimal('1234.567').adjusted()=3.- Target exponent =
3 - 3 + 1 = 1. - Quantize to
Decimal('1e1'), which is 10. - With
ROUND_HALF_UP, 1234.567 becomes 1230.
Decimal('1234.567').quantize(Decimal('1e1'), rounding=ROUND_HALF_UP)
# Decimal('1.23E+3')
Result: 1.23 × 103, or 1230 in positional notation.
Example 2: 0.0012345 to 3 significant figures
Decimal('0.0012345').adjusted()=-3.- Target exponent =
-3 - 3 + 1 = -5. - Quantize to
Decimal('1e-5'). - Result:
0.00123.
Decimal('0.0012345').quantize(Decimal('1e-5'), rounding=ROUND_HALF_UP)
# Decimal('0.00123')
Example 3: 2.5 to 1 significant figure
Under ROUND_HALF_EVEN, 2.5 rounds to 2 because the retained digit 2 is even. Under ROUND_HALF_UP, 2.5 rounds to 3. This is the classic tie-breaking difference between banker’s rounding and half-up rounding.
Counter-Examples
- Float contamination:
Decimal(2.675)is actually2.674999999999999822.... Quantizing to two decimal places withROUND_HALF_UPgives2.67, not2.68. UseDecimal('2.675'). - Double rounding: Rounding 1.245 to 2 significant figures directly gives
1.2. But rounding first to 3 significant figures gives1.25, then to 2 gives1.3. That is a double rounding error. - Decimal places vs significant figures:
f'{1234.567:.2f}'gives1234.57, which has two decimal places, not two significant figures. The correct 3-significant-figure result is1230. - Insufficient context precision:
quantize()can fail for very large coefficients ifctx.precis too low. Always raise precision before quantizing.
Convention Comparison Table
| Convention | Tie-breaking | Typical use | Python constant |
|---|---|---|---|
| Half-even (banker’s) | Round to nearest even last digit | ASTM E29, ISO 80000, Python default | ROUND_HALF_EVEN |
| Half-up | Always round 5 up | Everyday math, some finance | ROUND_HALF_UP |
| Half-down | Always round 5 down | Specialized fields | ROUND_HALF_DOWN |
| Truncation | Drop extra digits | Conservative limits | ROUND_DOWN |
Standards Citation
ASTM E29-13a, Section 6.2 recommends rounding ties to the nearest even digit for test data conformance. ISO 80000-1:2009, Clause 7.3.3 gives the same rule for general quantities and units. JCGM 100:2008 (GUM), Clause 7.2.6 advises reporting uncertainty to at most two significant digits. NIST SP 811, Section 7.9 provides rounding guidance for SI values. Python’s default ROUND_HALF_EVEN is therefore a defensible choice for standards-aligned work.
GUM 7.2.6: The numerical values of the estimate and its uncertainty should not be given with an excessive number of digits. In practice, uncertainty is often reported to two significant digits.
Common Mistakes
- Using
Decimal(float)instead ofDecimal('string'). - Confusing decimal places with significant figures.
- Forgetting to increase context precision before
quantize(). - Using built-in
round()for significant figures; it rounds to decimal places and uses binary floats. - Double rounding: rounding twice instead of once to the final precision.
- Assuming trailing zeros are never significant; in measurement they can be.
Quick Reference Table
| Task | Code pattern |
|---|---|
| Import tools | from decimal import Decimal, localcontext, ROUND_HALF_EVEN |
| Find target exponent | exp = x.adjusted() - n + 1 |
| Build quantizer | q = Decimal('1e{}'.format(exp)) |
| Round to n sig figs | x.quantize(q, rounding=ROUND_HALF_EVEN) |
| Use high precision | with localcontext() as ctx: ctx.prec = 50 |
Related Rules
FAQ
Does Python's decimal module round to significant figures automatically?
No. Use Decimal.quantize() with an exponent computed from x.adjusted() - n + 1.
Which rounding mode should I use for significant figures?
ROUND_HALF_EVEN is the Python default and aligns with ASTM E29 and ISO 80000 for tie-breaking. Some fields prefer ROUND_HALF_UP.
Why does Decimal(2.675) round incorrectly?
Decimal(2.675) converts a binary float, which is not exactly 2.675. Use Decimal('2.675') instead.
How many significant figures should uncertainty have?
GUM JCGM 100:2008, Clause 7.2.6, suggests uncertainty is often reported to two significant digits.

Leave a Reply