Split a Decimal total with an explicit remainder rule
Splitting a rounded amount is an allocation problem: dividing first can produce more fractional minor units than can be paid. This example starts with 10.00, converts it to integer cents, and uses divmod() to obtain a base allocation of 333 cents and one remaining cent for three recipients. Its explicit rule awards leftover cents in recipient order, producing 3.34, 3.33, and 3.33.
Keeping the allocation arithmetic in integer minor units makes the remainder rule easy to inspect. Decimal("0.01") is used only to convert the final cent counts back into decimal amounts. The assertions establish three useful invariants for this input: every allocation has two decimal places, the allocations sum to the original total, and the first recipient gets the selected extra cent.
Recipient order is a policy decision, not a mathematical necessity. A real system might rotate priority, use weighted shares, allocate by largest remainder, or prohibit a split where a minimum payment would be violated. It must also define behavior for negative totals, non-two-place currencies, and amounts that are not an exact multiple of the selected minor unit. This example does not decide those policies.
The code uses standard-library decimal, available in supported Python 3 releases; the official decimal documentation explains decimal values and their exponent representation.
AI-assistance disclosure: This article was drafted with AI assistance and should be reviewed for the application's rules.
from decimal import Decimal
total = Decimal("10.00")
minor_unit = Decimal("0.01")
recipients = 3
cents = int(total / minor_unit)
base_cents, remainder = divmod(cents, recipients)
shares = [Decimal(base_cents + (index < remainder)) * minor_unit
for index in range(recipients)]
assert shares == [Decimal("3.34"), Decimal("3.33"), Decimal("3.33")]
assert sum(shares, Decimal("0.00")) == total
assert all(share.as_tuple().exponent == -2 for share in shares)
print(", ".join(f"{share:.2f}" for share in shares))
3.34, 3.33, 3.33
Comments
Post a Comment