Why Using Float for Money Calculations Is Dangerous
Here is a quick experiment. Open your browser's console (F12) and type:
0.1 + 0.2
What do you expect? 0.3.
Here is what you actually get:
0.30000000000000004
That is right. 0.1 + 0.2 is not equal to 0.3 in floating point arithmetic. And if you are building anything that handles money, this tiny error can cost millions.
Key sources: IEEE 754 specification, "What Every Computer Scientist Should Know About Floating-Point Arithmetic" by David Goldberg, and practical engineering wisdom from Stripe, Modern Treasury, and financial systems architects.
The Problem: Binary Cannot Do Decimals
Computers think in binary (base 2). Humans think in decimal (base 10).
Here is the catch: some decimal numbers that look perfectly innocent, like 0.1, simply cannot be represented exactly in binary. It is like trying to write 1/3 as a decimal. You get 0.33333... forever. There is always a tiny remainder.
In binary, 0.1 becomes an infinitely repeating fraction. The computer has to cut it off somewhere, and that creates a rounding error.
// Go example — same problem
var a float64 = 0.1
var b float64 = 0.2
fmt.Println(a + b) // 0.30000000000000004
The error per operation is tiny, about 1 part in 10^16. But that is enough to break financial systems.
How Small Errors Become Big Problems
One transaction with a 0.00000000000000004 error is negligible. But consider a system processing millions of transactions.
- Stripe processes over 500 million transactions per day
- A trading firm executes 10,000 microtransactions per second
- A payroll system calculates tax deductions for 50,000 employees
Those tiny errors accumulate. They do not cancel out. A rounding error of $0.00000004 per transaction becomes thousands of dollars in discrepancies.
Real world disaster: The Vancouver Stock Exchange lost 25% of its computed value in the 1980s. Not because of a market crash, but because of floating point rounding errors in their index calculation.
The Three Biggest Dangers
1. Silent corruption
Floating point errors do not throw exceptions. Your code runs perfectly fine while producing wrong results. No crash, no warning. The first sign of trouble might be an auditor asking why your books do not balance.
2. Impossible equality checks
if total == 10.00 {
// This may never execute
}
Because total might actually be 10.000000000000004 or 9.999999999999996, the equality check fails silently. The logic simply does not run, and you have no idea why.
3. Unpredictable rounding
Financial systems have specific rounding rules (round half up, round half down, round half to even — called "banker's rounding"). Floating point does not follow any of these consistently. Your tax calculation might round one way on Monday and a different way on Tuesday.
The Right Ways to Handle Money
Option A: Integer (cents)
Store everything in the smallest unit of currency:
// $10.50 becomes 1050 (cents)
var price int64 = 1050
var quantity int64 = 3
var total int64 = price * quantity // 3150 cents = $31.50
Pros: Exact, fast, no precision issues. Cons: Need to handle division carefully (remainders matter).
Option B: Decimal type
Many languages have a decimal type:
// Go does not have a native decimal — use shopspring/decimal
price := decimal.NewFromFloat(10.50)
quantity := decimal.NewFromInt(3)
total := price.Mul(quantity) // Exactly 31.50
Pros: Handles all decimal operations correctly. Cons: Slower than integers, requires a library in some languages.
Option C: Specialized money libraries
Libraries like github.com/Rhymond/go-money in Go handle currency formatting, rounding rules, and precision automatically.
Summary
| Float/Double | Integer (cents) | Decimal type | |:--------------:|:-----------------:|:--------------:| | Rounding errors | Exact | Exact | | Equality fails | Works | Works | | Silent bugs | Clear math | Clear math | | Good for games | Good for money | Good for money |
Rule of thumb: If it touches money, do not use float or double. Use integers (cents) or decimal types. Your future self, and your auditor, will thank you.