Floating-Point for Money
Storing money in binary floating point introduces representation errors that compound until totals fail to reconcile to the cent, a compliance risk in finance. Use exact decimal types or integer minor units, with centralized rounding and explicit currency.
This anti-pattern stores currency amounts in binary floating-point types (float, double, real). Binary floating point cannot represent most decimal fractions exactly — 0.1 has no finite binary form — so monetary values acquire tiny representation errors that compound across arithmetic. For money, where every cent must reconcile, this is unacceptable.
Why It Happens
Floating point is the default numeric type in many languages and the first thing reached for to hold a price. The errors are minuscule on a single value, so they pass casual testing. Developers underestimate how quickly rounding error accumulates over many operations, and how strict financial reconciliation actually is. The type seems adequate until the books fail to balance.
Why It Hurts
Because values like 0.1 and 0.2 are stored approximately, sums drift: 0.1 + 0.2 famously is not exactly 0.3 in binary floating point. Across thousands of transactions, interest calculations, tax computations, and currency conversions, these errors accumulate into discrepancies of cents or more. Totals fail to reconcile, invoices are off by a penny, and audits flag mismatches. In regulated financial contexts this is a compliance and trust problem, not a cosmetic one. Comparisons for equality become unreliable, and rounding behavior is hard to control consistently.
Warning Signs
- Monetary columns or variables use float or double types.
- Reports and ledgers show penny-level discrepancies that no one can explain.
- Sums of line items do not equal the recorded total.
- Equality comparisons on amounts behave inconsistently.
Better Alternatives
Use an exact decimal type. In the database, store money in a fixed-precision NUMERIC/DECIMAL column with defined scale, which represents decimal fractions exactly and rounds predictably. Alternatively, store amounts as integers in the smallest currency unit — cents or the equivalent minor unit — and format for display, which sidesteps fractional representation entirely. In application code, use the language's decimal or fixed-point type, never binary floating point, for all monetary arithmetic. Always pair amounts with an explicit currency.
How to Refactor Out of It
Change monetary columns from float types to DECIMAL with the correct precision and scale, or to integer minor units, and migrate existing data with careful, documented rounding. Replace floating-point money handling in application code with a decimal type end to end, including serialization. Define and centralize rounding rules so they are applied consistently. Add reconciliation tests that sum line items and assert exact equality with recorded totals. After migration, verify historical reports now balance to the cent.