The Floating-Point Problem: Why Your Sums Are Wrong
0.1 + 0.2 is not 0.3 in any language. Floating-point arithmetic drifts, cancellation destroys precision, and your grand totals quietly inherit the damage. Here is how it works and what to do.
Open a console and evaluate 0.1 + 0.2. The answer is 0.30000000000000004. This is not a bug in your browser, your spreadsheet or your language — it is the unavoidable cost of representing decimal numbers in binary floating point, and every data pipeline inherits it.
Why It Happens
Numbers in JavaScript, Python and most databases are IEEE 754 double-precision floats: 53 bits of significand, giving roughly 15–17 significant decimal digits. Most decimal fractions — 0.1 included — do not have a finite binary representation, so they are stored as the nearest approximation. The approximation error is tiny for one number, but it compounds:
- Sums accumulate error. Adding a million small values can drift by enough to break a reconciliation to the cent.
- Cancellation destroys precision. Subtracting two large, nearly equal numbers —
1e16 + 1 - 1e16— leaves only the error:1simply vanishes because it is below the precision of1e16. - Percentages and ratios amplify. Computing a share as
part / totalthen multiplying back is a recipe for "99.99999999999999%".
The Practical Fixes
You rarely need arbitrary-precision arithmetic — you need to control where error is allowed to accumulate:
- Round at the boundary, not during the computation. Keep full precision in intermediate steps and round once for display or storage. Rounding every intermediate total compounds the opposite way.
- Sum in a stable order. Add small values before large ones (or use a compensated method). Naive sequential summation of many small numbers is the worst case.
- Use Kahan summation for long totals. Kahan's compensated summation keeps a running correction term for the error lost in each addition. It costs a few extra operations per step and routinely recovers the cents that naive summation drops:
sum = 0; c = 0
for x in values:
y = x - c
t = sum + y
c = (t - sum) - y
sum = t
- Money should not be a float at all. Store integer minor units (cents, satoshis, öre) and divide by 100 only for display. This single convention eliminates a whole class of accounting bugs.
- Use tolerance, not equality. Never compare floats with
==after arithmetic. Compare withabs(a - b) < 1e-9, or round both to the precision you actually trust.
What This Means for Analysis Tools
When a client-side tool like KPI Master computes totals, means and shares from your file, the same IEEE 754 rules apply — the browser is not magic. The mitigation is the same one a careful spreadsheet or database uses: compute in double precision, round deliberately at the output boundary, and use compensated summation where the totals are long. If your exported CSV says a column of monthly revenues sums to 1,000,000.0000001, the data was probably fine — the floating-point layer just told you the truth about binary arithmetic.