Exchange-rate precision and rounding

Keep decimal exchange rates accurate, round monetary results once, and understand half-even rounding with reproducible Python examples.

A white narwhal beside an abacus and a tray of currency tokens.

Rate precision is different from amount precision

A USD result may be displayed with two decimal places while the rate used to calculate it needs several more. Trimming the rate to match the currency’s display format changes the input to every conversion.

Using the illustrative EUR-to-USD rate 1.08765 for EUR 250 gives USD 271.91250 before rounding, or USD 271.91 to two places. Rounding the rate first to 1.09 produces USD 272.50: a 59-cent difference in the displayed results.

Half-even resolves exact ties

When the discarded portion is exactly halfway, half-even chooses the neighbouring value whose last retained digit is even. At two decimal places, 1.005 becomes 1.00 and 1.015 becomes 1.02. This rule is different from always rounding a tie upward.

These are exact decimal examples. Values just above or below the midpoint are not ties. The rounding mode must be part of the calculation policy, rather than whatever a formatting function happens to use.

Parse decimal strings directly

Python’s decimal module can construct decimal values from strings and quantize a result to a specified increment. The following local example mirrors a two-decimal half-even amount calculation; it does not fetch a live rate.

Runnable Python example
from decimal import Decimal, ROUND_HALF_EVEN, localcontext

with localcontext() as ctx:
    ctx.prec = 40
    amount = Decimal("250.00")
    rate = Decimal("1.08765")
    result = (amount * rate).quantize(
        Decimal("0.01"), rounding=ROUND_HALF_EVEN
    )

print(result)  # 271.91

Choose the currency’s minor unit

Do not apply two decimal places to every target currency. Narwhal’s conversion operation uses the target’s ISO 4217 minor unit. For example, its documented USD-to-JPY response returns a whole-yen converted amount.

Use the current currency metadata or the conversion endpoint’s returned amount. Cash-denomination rounding, merchant display rules and accounting policies may impose additional requirements; they should be explicit choices separate from rate precision.

Prevent avoidable loss of precision

Converting a decimal string through binary floating point can lose information before a decimal library sees it. Keep the original string at the API boundary and choose sufficient decimal working precision for the largest supported amount and rate.

  • Keep the received rate string for audit and reproduction.
  • Do intermediate multiplication or division at working precision; round the final amount once.
  • Specify the rounding mode and target increment.
  • Format only for display, and never feed a formatted label back into a calculation.

Reconcile using the same inputs

If a local calculation differs from Narwhal’s converted field, compare the input amount, full returned rate, target minor unit and rounding mode. Check those before assuming the result is wrong.

Converting a rounded amount back to the original currency can lose a small amount even without a spread. For batches, rounding each line and then summing can also differ from summing unrounded lines and rounding once. Choose the rule required by the application and record it consistently.

Sources and references

Published . Last reviewed .