+++
title = "Decimal arithmetic does not choose a rounding policy"
description = "Decimal arithmetic cannot decide when to round. A worked example shows why line-item and aggregate rounding produce different balances."
date = 2026-09-11
draft = false
[taxonomies]
topics = ["scientific-statistical-languages", "pricing-numerical-optimization", "core-ledgers-account-processing"]
kinds = ["advanced-explainer"]
[extra]
tier = "public"
schema_type = "Article"
article_class = "advanced-explainer"
related_concepts = ["decimal arithmetic", "rounding mode", "quantization", "residual allocation", "reconciliation"]
sources = ["https://docs.python.org/3/library/decimal.html", "https://www.postgresql.org/docs/current/transaction-iso.html"]
source_details = [{ title = "decimal — Decimal fixed-point and floating-point arithmetic", publisher = "Python Software Foundation", url = "https://docs.python.org/3/library/decimal.html", checked = "2026-09-09" }, { title = "Transaction Isolation", publisher = "PostgreSQL Global Development Group", url = "https://www.postgresql.org/docs/current/transaction-iso.html", checked = "2026-09-09" }]
faq = [{ question = "Does decimal arithmetic eliminate every rounding difference?", answer = "No. Different rounding stages or policies can produce different recorded amounts." }, { question = "Is the smaller total automatically correct?", answer = "No. The operative calculation policy determines the required total." }, { question = "Does a cent-level reconciliation break prove the inputs differ?", answer = "No. Identical exact inputs can produce different totals when rounding occurs at different stages." }]
evidence_as_of = "2026-09-09"
related_articles = ["positions-balances-risk", "lineage-quality-reconciliation", "notebook-to-model-service"]
category_slug = "software-models"
category_name = "Software & models"
+++

A financial rounding policy specifies the precision, rounding rule and calculation stage at which amounts become payable or recordable. Decimal representation preserves specified decimal values; it does not select the policy that turns those values into recorded amounts.

## Representation and calculation context

Binary and decimal arithmetic represent numbers differently. Python’s decimal arithmetic supports exact decimal representation within the relevant precision and context rules. Its accounting usefulness does not make every decimal operation infinitely precise or every financial convention automatic.

The calculation contract includes input units, allowed precision, rounding mode and output scale. A service receiving 0.005 dollars must know whether that amount is an intermediate value, a final line amount or part of an aggregate that will be rounded later.

Converting between representations is also a boundary. An exact decimal input and a decimal value obtained from an already rounded or otherwise altered input are different starting points. The type name alone does not identify the input history.

## Rounding before or after aggregation

Take three exact charges of 0.005 dollars. Assume amounts are recorded to cents using half-up rounding. Half-up rounds each positive half-cent to the next cent under this example’s scale.

Rounding each charge first gives 0.01 dollars per charge and a total of 0.03 dollars. Summing the exact charges first gives 0.015 dollars, which rounds to 0.02 dollars. Both paths use decimal arithmetic and the same rounding mode. The difference arises from the stage at which rounding occurs.

The calculation can be written as two separate operations: sum of rounded line amounts, and rounded sum of unrounded line amounts. They are not interchangeable transformations. An API contract that specifies only a decimal field leaves that choice unresolved.

## Residuals and posted amounts

A workflow that computes one aggregate amount and then allocates it across lines needs a rule for any residual. If the aggregate is 0.02 dollars, assigning 0.01 dollars to all three lines would produce allocations totaling 0.03 dollars. The line amounts would not reconcile to the aggregate.

An allocation rule can determine which line carries an adjustment, but that rule is another part of the specification. A different valid allocation policy can change individual line amounts while preserving the same aggregate. The article’s example does not select the operative policy for any real contract.

Reversals also need an identified relationship to the original recorded amount. Recomputing an earlier charge under a changed policy can produce a different value from the amount that originally posted. Preserving the original value and policy version makes that difference explainable.

## Calculation contracts across services

A calculation service, loan-servicing application and ledger can all use decimal fields while implementing different rounding stages. Reconciliation then needs the original inputs, intermediate precision, line or aggregate rule and policy version.

A difference of one cent does not identify which side is wrong. The authoritative contract or accounting rule determines the expected result. Database atomicity protects the configured operation from partial commitment; it does not repair an incorrect rounding rule inside that operation.

## Scope of the rounding example

The example uses positive charges in a currency recorded to cents and an explicitly stipulated half-up policy. Other currencies, products and agreements can require different scales or rules. The stable conclusion is that representation, rounding mode and rounding stage are separate choices, each capable of changing a financial result.

## Questions about decimal arithmetic

### Does decimal arithmetic eliminate every rounding difference?

No. Different rounding stages or policies can produce different recorded amounts.

### Is the smaller total automatically correct?

No. The operative calculation policy determines the required total.

### Does a cent-level reconciliation break prove the inputs differ?

No. Identical exact inputs can produce different totals when rounding occurs at different stages.
