Money Math
Computers are bad at decimals: to them $0.10 + $0.20 is not $0.30. Split $100 three ways and a cent goes missing. Here is how to count money so it adds up.
Try it
Four small calculators. Each one is a mistake that looks fine on a single payment and costs real money over a month.
Computers store 0.10 only approximately. One payment hides it; a month of them, added up and compared with the bank, does not. Count in whole cents and the problem disappears.
The three rules
Whole numbers, smallest coin
Store 4999, not 49.99, and always remember the currency. No decimals anywhere in the database.
Round once, on purpose
Where you round, which way, and who gets the leftover cent is a business decision. Write it down, do it in one place, store the result.
Add the dot at the very end
The number becomes '$49.99' only when a person is about to see it. Typing goes the other way: text straight to whole cents.
The code
The bad version does arithmetic on decimals and converts for the card company at the end. The good version counts in whole numbers from start to finish and adds the dot only for display.
Show the code anyway
const price = 19.99;const total = price * qty; // 59.97000000000001const share = Math.round(total / 3 * 100) / 100;const minor = Math.round(total * 100); // JPY: 100× too muchawait stripe.charges.create({ amount: minor });type Money = { minor: bigint; currency: string };export function split(total: bigint, parts: number): bigint[] { const base = total / BigInt(parts); let rem = total - base * BigInt(parts); return Array.from({ length: parts }, () => base + (rem-- > 0n ? 1n : 0n));}export function minorDigits(currency: string): number { return new Intl.NumberFormat("en", { style: "currency", currency }) .resolvedOptions().maximumFractionDigits; // USD 2, JPY 0, KWD 3}// Postgres: amount_minor BIGINT NOT NULL, currency CHAR(3) NOT NULL// Never NUMERIC with implicit scale, never FLOAT, never "decimal in the ORM".Still curious?
A few more questions people ask. Open what you need.
Can't the database just store decimals exactly?
It can. The trouble is every other piece of software between the database and the screen, which turns decimals back into approximations. Whole numbers survive the trip.
Who gets the leftover cent?
Whoever the rule says. The rule matters less than having one and applying it the same way everywhere.
Is it ever fine to use decimals?
For a chart or a rough estimate, yes. For anything that will be added up, compared with the bank, or sent to the card company, no.
What does the card company want to receive?
Whole numbers in the smallest unit, plus the currency. If your own system counts the same way, there is nothing to convert and nothing to get wrong.