0.1 + 0.2 ≠ 0.3 🤯
Ari
Floating-point numbers cannot represent many common decimal values exactly. They store approximations. These tiny deviations accumulate, causing comparison failures, inconsistent balances, and unpredictable behavior in financial code. The result is not a “bug” but a mathematical consequence of how numbers are represented inside computers.
Example 💻
0.1 + 0.2
→ 0.30000000000000004
// Many people will expect 0.3 here.
0.1 * 7 === 0.7
→ false
// Counterintuitively evaluates to "false"
This looks trivial, but in systems that transfer, sum, or reconcile money, a difference of even one cent is enough to break audits, cause failed comparisons, or create irreconcilable ledger states.
Why it happens 🔍
In general mathematics, this behavior is described as a non-terminating repeating expansion. A fraction becomes non-terminating when it cannot be represented cleanly in a given number system (a “base”). Instead of ending, its digits continue forever in a repeating pattern.
Why bases matter 🔢
A positional number system can only express fractions cleanly when the fraction’s denominator is composed of that system’s prime factors. Any denominator involving other primes produces an infinite repeating representation. Computers use base-2, which has digits 0 and 1. When humans write numbers, we use base-10, which has digits 0–9.
-
Base-2 has prime factor 2 only.
- Fractions like 1/10 (0.1 in decimal) contain the prime 5 and therefore cannot terminate in binary.
1/10 → 0.00011001100110011… (repeats infinitely)
- Fractions like 1/10 (0.1 in decimal) contain the prime 5 and therefore cannot terminate in binary.
-
Base-10 has prime factors 2 and 5.
- Fractions like 1/3 cannot terminate because 3 is not made of 2 or 5
1/3 = 0.333333333... (repeats infinitely)
- Fractions like 1/3 cannot terminate because 3 is not made of 2 or 5
The key point: in a binary system, fractions requiring prime factors other than 2 become infinite by mathematical necessity.
IEEE 754 ⚙️
So because certain fractions are infinite, and the fact that computers cannot store infinite sequences we rely on a universal rule for cutting the sequence off. That’s where the “Floating Point Formats” standard comes into play.
The IEEE 754 standard (introduced in 1985) defines:
- how floating-point numbers are stored in a fixed number of bits.
- how infinite binary expansions must be rounded.
- how arithmetic and comparisons behave.
- how special values (NaN, ±∞) should work.
The standard simply defines how to store the closest possible approximation using finite hardware. Because all modern languages implement IEEE 754, the behavior is consistent: JavaScript, Python, Java, C#, Go, Rust, and most likely any other programming languages that you know of will all produce the same 0.30000000000000004 when adding 0.1 + 0.2 by default.
Practical Guidance 💰
Floating-point math is fundamentally incompatible with money because money requires exactness, and floating-point representation guarantees approximation.
Use integer arithmetic for currency ✔️
Represent amounts in the smallest indivisible unit (cents, øre, etc.). Perform all calculations on integers and convert to a readable currency format only when displaying values.
Refer to ISO-4217 to see each currency’s minor unit (e.g., USD → 2 decimals → cents; JPY → 0 decimals → yen).
// ✅ Do
const durationMonths = 5
const priceCents = 2330 // smallest unit
const totalCents = priceCents * durationMonths
// ⛔️ Don't
const durationMonths = 5
const priceDollars = 23.30 // float: imprecise
const total = priceDollars * durationMonths
Use integers everywhere inside your system. Only at the final output boundary (as in the front-end) should you convert back to a human-readable string such as “23.30”.
Decimal Types 🔢
For systems that deal with extreme precision, many languages offer dedicated Decimal or BigDecimal types. These typically store numbers using a base-10 representation internally, guaranteeing exactness for all decimal values, at the cost of being slower than native floats. For instance:
- C# has a built-in data type
decimal - Starting with Python 2.4, the standard library includes a
Decimalclass in the moduledecimal. - Java’s standard library includes a
java.math.BigDecimalclass.
Summary 📝
Many decimal fractions become infinite, repeating sequences when converted to binary.
- IEEE 754 defines how to store finite approximations, not the math that causes the repetition.
- Floating-point values introduce unavoidable rounding error.
- Financial systems require exact arithmetic, which floats cannot provide.
- Use integers (cents) or a dedicated decimal library for all money-related logic.
This eliminates drift, prevents comparison failures, and ensures every cent is accounted for.