The Floating Point Drift You Don't Notice Until It Breaks Your Ledger
It's not a particularly famous problem by any academic standard, but if you've ever shipped financial software and watched $0.07 vanish from a transaction after six decimal places of division, you've met it. The "broke math problem" isn't a single equation — it's the cluster of edge cases that surface when you let IEEE-754 floating point arithmetic handle currency. Here's how I dealt with it on a recent project. We were processing batch payouts across roughly 40,000 rows. Each row went through a percentage split calculation involving a repeating decimal derived from 1/3. The first run produced a total that was $1.23 short of the expected sum. Nobody was rounding manually. No one had touched the formatting layer. The discrepancy was pure binary float behavior: 0.3333333333333333 in double precision, multiplied by values that themselves could not be represented exactly in base-2. The workaround was not pretty but it was reliable. I switched every monetary intermediate to integer cents internally. The percentage splits became integer operations on scaled values, and I only converted back to floating point at the very last second for display. I added a reconciliation check that summed all row outputs and compared them to the expected total, flagging anything beyond a half-cent tolerance. It caught the drift and eliminated it in one pass.
What people miss initially is that the problem isn't limited to dollars. Anything that needs exact decimal representation — crypto contract settlements, tax brackets, split billing across vendors — runs into the same wall. The moment your system treats a decimal quantity as a binary float, you open the door. You don't have to do anything unusual. Normal multiplication is enough.
Why it happens and what most people get wrong
Floating point stores numbers in base-2. Many common decimal fractions are infinite repeating sequences in binary, so the computer stores the closest approximation it can. That approximation is usually off by something on the order of 1e-16 relative to the magnitude of the number. For a single operation that's invisible. In a loop with thousands of operations, it adds up. Common mistake number one is thinking that casting to a decimal library at the output stage fixes it. If the damage happened during the calculation, you're just formatting a wrong answer. The fix has to happen before the arithmetic, not after. Common mistake number two is using Math.round() at arbitrary points to "clean up" values. Rounding mid-calculation introduces its own bias. If you round aggressively, you systematically skew your totals in one direction. I learned that the hard way on a project where monthly variance drifted upward by about 0.4% over six months. The culprit was consistent early rounding on fractional cent values.
Get the Full Details

The practical fix, stripped down
Here's what I use now as a default pattern: Keep all monetary state as integers. Represent amounts in the smallest unit — cents, satoshis, whatever your domain's atomic unit is. Perform addition, subtraction, and multiplication exactly. Division is the tricky part, and that's where you need a defined rounding strategy, not a guess. For division, pick a rounding mode and stick with it. Half-up is common for consumer-facing systems. Half-even is better when you're doing repeated divisions because it reduces systematic bias. In the project I mentioned above, switching from half-up to half-even cut the residual drift from $1.23 down to $0.02 across the same 40,000-row batch.
After the integer math, if you must convert to float for an API response or a report, do it once per value, never twice. Storing a float and then converting it again later re-introduces the same ambiguity you were trying to escape.
Tools that make this easier
If you're working in JavaScript, libraries like bignumber.js or decimal.js handle the arithmetic correctly without requiring you to refactor everything to integers. They're slower than native floats, but for transactional workloads the difference is measured in microseconds per call. In Python, decimal.Decimal is the built-in path and it's perfectly adequate if you initialize it from strings rather than floats. The string initialization matters because passing a float into Decimal(0.1) gives you the binary approximation, not the decimal one. I typically benchmark any library I introduce against a reference implementation that uses integer math on a known dataset. If the outputs diverge by more than one atomic unit across a thousand operations, I don't ship it.

Where this approach breaks down
Integer math is not a universal cure. It struggles when your calculations involve irrational quantities or when you need true continuous precision — things like option pricing models, physical simulations, or any kind of Monte Carlo sampling where the underlying distribution demands high-precision floats. In those domains, the broken math problem looks different. You're not losing pennies; you're accumulating numerical instability through repeated operations on nearly singular matrices or exponentially growing error terms. If your work falls into that category, the fix isn't integer conversion. It's interval arithmetic, arbitrary-precision libraries, or restructuring the algorithm to reduce the condition number of your operations. That's a separate problem space. For ordinary financial and commercial computation, staying in integer land until the final output step is usually sufficient and dramatically safer than hoping the float will cooperate.
Broke Math Problem examples you'll recognize
Here are two quick cases I see constantly in code reviews: A rounding function that chains parseFloat(value.toFixed(2)) inside a loop. This looks correct until you introduce a value like 2.675, which in binary float is actually 2.674999999999999822..., so toFixed produces 2.67 instead of 2.68. The lost half-cent compounds. A database column declared as DECIMAL(19,4) but the application layer reads it back as a float, performs a calculation, and writes it forward. The loss happens at the read, not the write. Migrating the application to use integer cents or a proper decimal type on access resolved the issue entirely for a payment reconciliation service I worked on. The database was never the problem.
Once you stop treating float as a default and start treating it as a specific tool with known failure modes, the whole class of errors becomes predictable. That's the actual takeaway from dealing with the Broke Math Problem repeatedly. It doesn't require cleverness. It requires a disciplined choice about when to leave base-10 alone and when to stay in integers.
