Why your accounting software should never store money as a decimal
A short tour of the bug that quietly loses cents, why it is unavoidable in binary floating point, and what correct software does instead.
Open a browser console and type 0.1 + 0.2. You will not get 0.3. You will get 0.30000000000000004.
This is not a bug in the browser. It is a direct consequence of how computers store fractional numbers, and it affects nearly every programming language. It is also the reason a financial system should never hold money in that format.
Where the error comes from
Binary floating point represents numbers as sums of powers of two. Some decimal fractions map onto that cleanly — a half, a quarter, an eighth. Most do not. One tenth is a repeating fraction in binary, in exactly the way one third is a repeating decimal in base ten. You cannot write it down exactly in finite space, so the computer stores the nearest value it can and rounds.
A single rounding error is around one part in ten quadrillion — invisible. The problem is that ledgers do not perform one operation. They perform hundreds of thousands, and the errors accumulate in ways that depend on the order the operations happened to occur in.
Why it matters in practice
The consequences are less dramatic than they are corrosive. A monthly statement whose total is off by a cent from the sum of its own rows. A reconciliation that will not close, for no visible reason. A balance sheet where assets miss liabilities plus equity by $0.02 — small enough that you are tempted to shrug, large enough that you can no longer say the books are correct.
What makes it particularly unpleasant is that the discrepancy is not reproducible in any obvious way. It depends on the sequence of arithmetic, so it appears and vanishes as data changes. There is nothing to point at.
What to do instead
Store money as an integer number of the smallest unit — cents for dollars — and never leave that representation until the moment you format something for a human to read.
$1,200.50 is stored as 120050. Adding, subtracting, and comparing are exact, because integer arithmetic is exact. Ten additions of 10 produce exactly 100, every time, in any order.
The one place needing care is division — splitting an amount several ways. $10.00 shared three ways is 333, 333, 333, which is 999. A cent has disappeared. The correct approach distributes the remainder deterministically, one cent at a time, so the parts always sum back to the original exactly. You must decide who gets the extra cent — but you must never allow it to evaporate.
How to tell whether software gets this right
You cannot inspect the storage format from outside. But you can look for the symptoms, and there are two good tests.
- Enter an amount with three decimal places, such as
12.345. Well-built software rejects it and says why. Software that silently accepts and rounds it has just made a decision about your money without telling you. - Check whether a report's stated total exactly equals the sum of the rows it displays. A one-cent discrepancy is the classic fingerprint.
None of this is difficult or clever. It is simply a decision that has to be made at the very beginning, because retrofitting it means rewriting every calculation in the system — which is why so much software never does.