Three dimes.
Not thirty cents.
Binary floats cannot store a dime: 0.1 has no exact representation. Charge three of them: the float ledger drifts and its checks start failing, while integer cents stay exact forever.
Naive, sums doubles
- balance
- $0
Integer cents, sums exactly
- balance
- $0
Charge a few dimes: both ledgers just add ten cents each time. Watch the third one. The float ledger drifted: the display rounds it away, but the equality check fails, and checks are what reconciliation runs on.
How integer money actually works read more
One tenth is a repeating fraction in binary, so a double stores the nearest representable number instead. The display rounds the drift away, which is exactly what makes it dangerous: the checks money runs on do not round. Equality, reconciliation against the processor, debits-equal-credits: all of them fail on a difference too small to print.
The fix is the schema, not the arithmetic:
amount DOUBLE -- 0.1 + 0.1 + 0.1 = 0.30000000000000004
amount_cents BIGINT NOT NULL -- 10 + 10 + 10 = 30, exactly, forever Integer minor units make every add and compare exact, in every language and database. When fractional precision is inherent, like FX rates, use DECIMAL, which is exact too. The one wrong answer is binary floating point.
The cost: formatting moves to the display layer, and minor units are per currency (yen has zero decimals, Bahraini dinar has three), so the amount travels with its currency code.