Backend
Storing money: integers, always
A short post about the one bug I refuse to debug twice, and the three places floating-point currency still sneaks into an otherwise careful codebase.
Mr. Rofiq Rahman · · 5 min read
0.1 + 0.2 === 0.30000000000000004. Everyone knows this. Everyone still ships a Float column called price at some point.
Store money as an integer in the smallest currency unit. 1,250 means ৳12.50. Convert once, at the edge, for display.
Where it still sneaks in
1. The percentage discount. subtotal * 0.85 gives you a float, and now you are rounding — and whether you round each line or the total changes the answer. Pick one, write it down, and put it in a function.
// Round half up, per line item, then sum. Documented because
// the finance team reconciles against this exact behaviour.
const discounted = (minorUnits: number, percent: number) =>
Math.round((minorUnits * (100 - percent)) / 100);
2. The CSV import. Someone exports from a spreadsheet, the column is 12.50, and parseFloat is right there. Parse to minor units with a string-based conversion, and reject anything with more decimal places than the currency allows.
3. The chart library. It wants numbers and it will happily average your integers into a float. That is fine for a chart — just never write the result back.
Currencies without two decimal places
Japanese yen has none. Kuwaiti dinar has three. If you are even slightly likely to go multi-currency, store the exponent next to the amount rather than assuming 100. Retrofitting that is a migration nobody wants to run.
It takes an extra hour on day one. The alternative is a reconciliation meeting where nobody can explain a four-taka discrepancy.