Blog

Nobody can pay 0.99 of a dram

Our settlement once told a group to transfer 1,541,315.99 ֏. The math was perfect. The instruction was impossible, because a dram has no cents you can hand over. Here is the bug, the fix, and why the fix never creates or loses a single unit of money.

By David Piliposyan ·

The bug

Here is the case that started this. A group kept its money in Armenian drams. Three members. One expense: someone paid 4,623,947.99 ֏, split equally three ways.

Three ways doesn’t divide evenly. Work in hundredths, the way the app does, and the total is 462,394,799. Divide by three and you get 154,131,599 with a remainder of 2. So two people owe 1,541,316.00 ֏ and one owes 1,541,315.99 ֏. The shares add back to the total exactly. Not one hundredth lost, not one invented.

That is the correct ledger. And it is exactly where the trouble started, because settlement took those numbers and told two people to pay the person who had covered the bill:

What settlement used to say

Ana → the payer1,541,316.00 ֏
Leo → the payer1,541,315.99 ֏

Read the second line again. Leo is being asked for 99 luma. There is no 99 luma. You cannot walk into a shop, a bank, or a friend’s flat and hand over 0.99 of a dram.

The arithmetic was right. The instruction could not be followed by any human being. For a bill-splitting app, that is the worse kind of bug, because nothing looks broken until someone tries to send the money.

Why a dram has two decimals and no cents

ISO 4217, the standard list of currency codes, gives the Armenian dram two decimal places. The hundredth of a dram is called the luma. On paper it exists.

In real life it has not been money for a very long time. No coin, no note and no bank transfer expresses it. Most software, though, asks the standard how many decimals a currency has and believes the answer. If we had done that, we would have rebuilt the exact bug we were trying to kill.

The dram isn’t alone. Three more currencies have the same gap between the standard and the street:

  • Hungarian forint (HUF): the fillér was withdrawn in 1999.
  • Indonesian rupiah (IDR): the sen.
  • Uzbek som (UZS): the tiyin.

Then there are currencies where the standard itself says zero decimals: the Japanese yen, the Korean won, the Vietnamese dong, the Icelandic króna, the Chilean peso, and a dozen others, including the CFA francs. With those, nobody argues.

So we did not derive the answer from the standard. We keep an explicit list of currencies where the smallest amount a person can hand over is one whole unit. Everything else, euros, dollars, pounds, keeps its cents.

The fix: snap the balances, not the transfers

Our first instinct was to round each transfer. That’s the wrong place.

Settlement works on balances: for each person, what they paid minus what they owed. It then matches the biggest debtor against the biggest creditor, and each transfer is the smaller of the two balances. A transfer is therefore always built out of balances and nothing else.

That gives a neat trick. If every balance is already a whole number of drams, every transfer is a whole number of drams automatically. So we round the balances once, before matching, and the rest takes care of itself.

In the real case above, nothing complicated happens. Leo’s balance of -1,541,315.99 rounds to -1,541,316. The person who paid was owed a fraction under 3,082,632, which rounds up to the whole number. The books still balance, and the plan becomes two payments of 1,541,316 ֏ each. Both payable. Record both and the group is at zero.

The part that needs care: the leftover dram

That example was kind. Rounding two balances in opposite directions happened to cancel out. Often they don’t, and that’s where you can lose or invent money by accident.

Here is a made-up case with exact numbers. Say three flatmates, Dav, Ana and Leo, and Dav pays a dinner of 1,000.00 ֏, split equally.

A thousand divided by three is not tidy. In hundredths: 100,000 / 3 is 33,333 with a remainder of 1. So one person owes 333.34 ֏ and the other two owe 333.33 ֏. Say Ana owes the extra hundredth.

Exact balanceRounded to whole drams
Dav+666.67+667
Ana−333.34−333
Leo−333.33−333
Total0.00+1

Round each one on its own and the balances add up to +1 instead of zero. We have invented a dram out of nothing. Someone would be asked to pay one dram too little, and the person owed would never see it.

So there is a second step, and it’s short. The total is off by one whole unit. We hand that unit back to a person, whoever rounding treated most generously.

Here that’s Ana. Rounding took her from owing 333.34 to owing 333, a gain of 0.34 for her, the biggest of the three. She absorbs the extra dram:

Final balance
Dav+667
Ana−334
Leo−333
Total0

How it ends

Ana → Dav334 ֏
Leo → Dav333 ֏

Two payments, both whole drams, and 334 + 333 is 667, exactly what Dav is owed.

Why it never creates or loses money

Three reasons, and you can check each one yourself.

  1. The balances still add up to zero. The correction step exists for exactly this. Whatever rounding adds or removes, we put back before any transfer is made.
  2. Every transfer is a whole number. Transfers are built from balances, and the balances are whole units.
  3. Nobody is more than one unit off. Each person ends up within a single dram of what the exact ledger says. The extra dram goes to one named person, not smeared across the group, and it goes the same way every time. Settlement that reshuffled who pays the odd dram between two refreshes would be worse than the fractions it replaced.

The exact, to-the-hundredth ledger isn’t touched. Every expense is stored as entered, and you can still export the whole thing. Snapping happens only when we tell you what to pay.

What this looks like if you split in drams, forints or yen

You don’t have to do anything. If the group’s currency is one of these, the balances you see, the figure on your overview and the amount in “Your plan” are the same whole number, so they can never disagree. For euros and dollars, nothing changes, because a cent is still a cent.

If you ever do this by hand, the habit is simple. Settle in the smallest thing you can actually pay. Decide in advance who covers the odd unit. Then check the transfers add up to what’s owed. The settle-up guide walks through the fewest-transfers method, and the multiple currencies guide covers what to do when the bill and the group use different ones. For a trip in Armenia, there is also the Armenia trip guide.

I like this bug because the fix is small and the lesson is not. A number being correct and a number being payable are two different tests, and a money app has to pass both.

Money that adds up and can be paid

kisir shows every balance and the fewest transfers that clear them, in amounts you can actually send. No account, no ads, free.

Get kisir