How to Handle Money in Code: Integer Cents Over Floating Point
Open the calculator built into almost any programming language and ask it the simplest question in arithmetic. What is 0.1 plus 0.2? You expect 0.3. What you get is 0.30000000000000004.
That tiny tail of error is harmless in most software and ruinous in one place: anywhere you handle money. So the first rule I follow when I handle money in code is absolute. Never store it as a floating point number. I store every amount as a whole number of cents, because that is the only way the math stays exact from the first entry to the final payslip.
The short version: Computers store decimals in binary, and most decimals have no clean binary form, so 0.1 + 0.2 comes out as 0.30000000000000004. Harmless for a pixel, fatal for a paycheck. The fix is to stop storing money as a decimal at all. Store it as a whole number of cents (forty seven dollars and thirty cents is 4730, not 47.30), keep the decimal only at the edges where people type and read numbers, decide your rounding rule once and write it down, and make the database refuse any amount that broke the rules.
That is not a quirk of one language. It is true in almost all of them. Computers store decimals in binary, and one tenth has no clean binary form, the same way one third has no clean decimal form. You write 0.1, the machine keeps the closest binary fraction it can, and the tiny error rides along in every sum after it.
For most software this never matters. Off by a fraction of a trillionth, nobody notices. Payroll is the opposite case. A paycheck that is off by a hundredth of a cent is a paycheck that is wrong, and a wrong number on a payslip becomes a phone call, then a spreadsheet argument, then a person who stops trusting the system that pays them. So I treat money as a thing the machine is not allowed to approximate.
Why payroll software should use integer cents, not floating point
The principle is one line. Money is never a decimal. Money is a whole number of cents.
A paycheck of forty seven dollars and thirty cents is not 47.30 anywhere in the system. It is 4730. An integer. Integers add, subtract, and multiply with no drift, because there is no fraction for the machine to fumble. The whole class of rounding ghosts is gone, because there is nothing after the decimal point to lose. There is no decimal point.
| Floating point (the default) | Integer cents (the rule) | |
|---|---|---|
| How $47.30 is stored | 47.30, an approximation | 4730, an exact whole number |
| What happens to errors | They ride along in every sum | There is no fraction to lose |
| 0.1 + 0.2 | 0.30000000000000004 | exactly 30 |
| On a payslip | Approximately right, which is wrong | Right |
This is the default I never accept. Most code reaches for a decimal type because a salary looks like a decimal to a human. It looks that way on the screen and nowhere else. Underneath, a salary is a count of cents, so I store it as a count of cents and let the math behave.
Keep decimals at the edges of the system
The decimal exists in exactly two places: at the edge where a person types a number in, and at the edge where a number gets shown back to them. Type 47.30 and the system turns it into 4730 on the way in. Ask to see it and it turns 4730 back into 47.30 on the way out. In between, where the real math happens, it is all integers, and the drift never gets a chance to start.
This is the part people skip, and it is the part that holds the whole thing up. A decimal that lives only at the boundary is a display format. A decimal that lives in the core is a leak. I keep the boundary thin and the core clean, so every calculation runs on values that cannot wobble.
Decide the rounding rule once
There is one place this gets sharp, and it is rounding. Take a monthly salary and break it into an hourly rate. Fifty thousand a month, twenty two working days, eight hours a day. Divide it out and you get 284.0909, and the nines never stop. You cannot pay someone a fraction of a cent, so the number has to round, and the only real question is which way.
I round half away from zero, the rule most people learned in school: half a cent goes up. Always the same direction, written down once, applied the same on every line, so the same input gives the same paycheck today, next month, and in an audit two years from now. The point is not which rule wins. The point is that one rule is chosen, it is stated, and the machine never gets a vote.
I lock this with a test built on the most famous case in computing. Add a tenth and a fifth, the 0.1 and 0.2 that breaks calculators, and the system has to land on exactly thirty cents. Not close. Thirty. If that test ever goes red, the build stops before a wrong number can reach a person.
Make the database refuse a bad number
A rule that lives only in code is a rule that holds until the code has a bad afternoon. So I push it down a layer and make the database itself refuse to hold a wrong number. The place that stores a paycheck carries a rule that the value can never go negative. If a stack of deductions would push someone below zero, the math is not allowed to write a negative paycheck. It clamps to zero, the remainder is recorded as money still owed, and that balance carries into the next run. Nothing gets silently swallowed.
The code enforces this. The database enforces it too. Two locks on the same door. The second lock is the one that proves itself on the day the first one fails, and a paycheck is exactly the kind of number you want guarded twice.
People ask why payroll has to be this strict. It is not strictness for its own sake. It is the gap between approximately right and right, and on a payslip approximately right is the same as wrong. Nobody is impressed that a total was off by a thousandth of a cent. They see a number that is wrong, and once a person catches the system being wrong about their own money, they check every number from then on. The entire value of the system is that they never have to.
So the rule stays small and absolute. Money is a whole number of cents, the decimal lives only at the edges, the rounding is decided once, and the database rejects anything that broke the rule on the way in.
A computer cannot promise you that 0.1 plus 0.2 is 0.3. So I stopped asking it to. I count in cents, and cents always add up.
Frequently asked questions
Why is 0.1 + 0.2 not equal to 0.3 in most programming languages?
Because computers store decimals in binary, and one tenth has no exact binary representation, the same way one third has no exact decimal one. The machine keeps the closest value it can, and that tiny rounding error accumulates, so 0.1 + 0.2 lands on 0.30000000000000004.
How should you store money in code?
As a whole number of the smallest unit, usually cents. Forty seven dollars and thirty cents becomes 4730. Integers add and multiply without any rounding drift, so the math stays exact from the first entry to the final total.
What is integer-cents and why use it for currency?
Integer-cents means representing every amount as a count of cents rather than a decimal number of dollars. It removes the entire category of floating point rounding errors, because there is no fractional part for the computer to approximate.
How do you handle rounding when splitting an amount?
Choose one rounding rule, state it explicitly, and apply it identically everywhere. A common choice is rounding half away from zero. What matters is not which rule you pick but that it is fixed and consistent, so the same input always produces the same result, including during an audit.
Should money validation live in the database too?
Yes. A rule enforced only in application code fails the moment the code has a bug. Enforcing it in the database as well, for example refusing to store a negative paycheck, gives you a second lock that holds when the first one slips.