Convert an integer minor-unit amount to Decimal safely
Also published in our primary archive.
Convert an integer minor-unit amount to Decimal safely
To convert 12,345 integer minor units at scale 2 to Decimal, construct from the integer and call scaleb(-2) inside a context whose precision can hold the integer coefficient. The corrected expression below returns exactly 123.45; it never first creates the binary float 123.45.
The failing expression divides the integer before constructing Decimal. That division produces a float, and Decimal(float_value) records the float’s stored value exactly, including its binary-representation noise. The correction starts with an exact integer conversion and shifts its exponent by the declared scale. The fixture prints the original mapping and asserts it is unchanged, so the calculation is a derived, non-mutating value.
scaleb() is still governed by the active decimal context. A precision that is too small can round the coefficient: for example, precision 2 cannot retain all five digits of 12345. This example sets a local precision of 5, the number of decimal digits in the input, before performing the shift. In production, validate that scale is the intended non-negative integer and choose precision suitable for the largest supported minor-unit value. scaleb() changes the exponent; it does not validate a currency or apply a display-rounding policy. Apply quantize() separately only when a specified output scale and rounding mode are needed.
The official decimal documentation describes exact construction from integers, exact conversion from floats, decimal contexts, and scaleb(). This example uses APIs available in supported Python 3 versions; no newer API is required.
AI assistance disclosure: this article was drafted with AI assistance and uses a synthetic, locally reproducible fixture.
from decimal import Decimal, localcontext
record = {"minor_units": 12345, "scale": 2}
float_based = Decimal(record["minor_units"] / (10 ** record["scale"]))
with localcontext() as context:
context.prec = len(str(abs(record["minor_units"])))
exact_amount = Decimal(record["minor_units"]).scaleb(-record["scale"])
assert record == {"minor_units": 12345, "scale": 2}
assert float_based != Decimal("123.45")
assert exact_amount == Decimal("123.45")
print(f"input: {record}")
print(f"float-based result: {float_based}")
print(f"corrected result: {exact_amount}")
input: {'minor_units': 12345, 'scale': 2}
float-based result: 123.4500000000000028421709430404007434844970703125
corrected result: 123.45