Split a hundred.
Lose a penny.
Splitting money across lines means rounding each share, and the leftover cents vanish. Split a few: the naive lines never sum back to what was charged, while largest remainder reconciles to the cent.
Naive, round each line down
- in lines
- $0
- lost
- $0
Largest remainder, spread the cents
- in lines
- $0
- lost
- $0
Split $100 a few times, then change the divisor: watch whether the receipt still sums to what was charged. Those cents are gone: every line rounded down and nobody got the remainder. Largest remainder hands it to the first lines.
How largest-remainder allocation actually works read more
$100.00 does not divide by 3. The naive split gives every line the same rounded share, so the lines sum to $99.99 and a cent belongs to nobody. One cent sounds harmless; at invoice volume it is a reconciliation break that auditors will find.
Largest remainder makes the rounding explicit: round every share down, then hand the leftover cents, one each, to the first lines:
const base = Math.floor(total / n); // 10000 / 3 -> 3333 cents, $33.33
const left = total - base * n; // 1 cent has no home yet
// hand the leftover cents, one each, to the first lines
const lines = Array(n).fill(base).map((c, i) => (i < left ? c + 1 : c));
// -> $33.34, $33.33, $33.33 (sums to $100.00 exactly) The lines always sum exactly to the total, whatever the divisor. The receipt shows the raw cents on purpose: the money here is already stored as integer cents, and the penny still gets lost. Integer storage makes every sum exact, so division is the only place rounding can happen; allocation is how you handle that one place. Storing money in floats breaks earlier and worse, and that is its own failure mode.
The cost: someone gets the extra cent, and that is a policy you must choose and document (first line, largest line, the fee line), because whoever it is, the same split must always produce the same lines.