Most developers meet floating point through a tiny act of betrayal. A calculation that should be boring produces a result that looks like a confession from the machine: close enough to be recognisable, wrong enough to be unsettling.
The famous example is not interesting because it is obscure. It is interesting because it is ordinary. The numbers are small, the operation is simple, and the result exposes a bargain that most programs make long before anyone starts thinking about missiles, stock exchanges, rockets, invoices, or thresholds.
# Command:
node -e "console.log(0.1 + 0.2); console.log((0.1 + 0.2) === 0.3)"
# Output:
0.30000000000000004
false
That example is old enough to have become a joke. It appears in interviews, conference talks, bug reports, and late-night debugging sessions where someone has just discovered that arithmetic, supposedly the reliable part of computing, has developed an attitude problem. The tempting explanation is that JavaScript is broken. It is not. ECMAScript specifies its ordinary Number type as an IEEE 754 double-precision 64-bit binary floating-point value, with a few language-level choices around NaN values.1
JavaScript is merely honest enough to show the scar.
The same underlying issue appears in C, Java, Python, Go, Ruby, Swift, and most other mainstream languages whenever they use binary floating point for decimal-looking values. Some languages display a shorter decimal string by default, which makes the result look friendlier. The stored value has not become exact. The display layer has simply decided not to show every awkward digit at the dinner table.
Why 0.1 refuses to fit
Computers do not store decimal fractions the way people write them. A decimal fraction is built around powers of ten. A binary fraction is built around powers of two. The fraction 1/2 fits neatly in binary as 0.1, and 1/4 fits as 0.01, but 1/10 does not terminate. Its denominator contains a factor of five, and no finite pile of halves, quarters, eighths, sixteenths, or smaller powers of two can add up to exactly one tenth.
That is not a language quirk. It is number theory doing paperwork.
IEEE 754 exists because computers need a standard way to represent real-number approximations, perform arithmetic on them, round results, handle exceptional values, and exchange those values between systems.2 The standard is one of computing's great practical compromises. It gives programmers a compact representation that can cover tiny fractions, enormous magnitudes, infinities, signed zero, and NaN, while still being fast enough for ordinary hardware. The price is that most real numbers are not represented exactly. They are rounded to the nearest representable value.
This is also why the phrase floating-point error can mislead. It sounds like the computer made a mistake after the fact, but the approximation begins at storage time. The moment a program accepts the decimal literal 0.1 into a binary64 number, it chooses the nearest available binary value. Later operations then combine approximations, round the result, and convert it back into a decimal string for human display. The printed surprise is the last symptom, not the first cause.
David Goldberg's 1991 survey remains the canonical tutorial because it refuses to let programmers pretend this is a curiosity at the edge of the language manual. Floating point is everywhere: ordinary programming languages expose it, processors accelerate it, compilers optimise around it, and operating systems have to respond to its exceptional cases.3 The surprise is not that floating point sometimes rounds. The surprise is that programmers keep treating rounded values as if they were exact decimal facts.
The tiny example hides a larger model. A binary64 value has finite precision. It stores a sign, an exponent, and a significand, then uses that combination to approximate a real number across a huge range. ECMAScript's 2022 specification states that all positive and negative integers whose magnitude is no greater than 2^53 are representable in the Number type, which is why integer arithmetic can feel perfectly solid right up to the point where a program crosses the safe boundary.1 This is brilliant for scientific and engineering calculation, where values may vary across many orders of magnitude and small relative error is often acceptable. It is a poor fit for domains where decimal exactness is the requirement rather than a formatting preference.
Money is the obvious example, but not the only one. Tax calculations, interest calculations, invoice totals, inventory counts, ledger balances, and threshold checks often need exact decimal or integer semantics. If the business rule says two values differ by one cent, the system should not quietly discover an extra binary residue and ask production support whether 0.30000000000000004 is close enough. That conversation has happened in enough codebases already. It does not improve with repetition.
When the small error stops being small
Small errors are not automatically dangerous. Engineers rely on approximation constantly. Sensor readings contain noise. Simulations discretise continuous systems. Graphics pipelines use floating point because the human eye will tolerate tiny imprecision in exchange for speed. The problem begins when software forgets which kind of wrong it has chosen.
The failure mode is usually accumulation, comparison, or conversion.
Accumulation is the quiet one. Add a tiny error once, and nothing interesting happens. Add it thousands or millions of times, feeding each rounded result into the next calculation, and the bias can become visible. One published retelling of the Vancouver Stock Exchange index describes an early-1980s index error caused by repeatedly truncating values rather than rounding them.4 It is worth handling carefully because it was not precisely the 0.1 + 0.2 problem. It was a repeated numeric policy error. That distinction matters, because imprecision folklore often mutates into whatever lesson the speaker wanted to teach that week.
Comparison is more familiar to application developers. A program calculates a value, compares it with a threshold, and then behaves as if mathematical equality were a reasonable expectation. Sometimes the bug is small: a checkout total fails to match, a progress bar never reaches exactly 100%, a test flickers on one platform and passes on another. Sometimes the bug lives in a risk model, a billing system, or a control loop where a borderline comparison decides whether the program keeps going.
Conversion is harsher. Values that are safe in one representation may not be safe in another. A wide floating-point value converted to a narrow integer can overflow. A high-precision calculation rounded into a lower-precision format can lose the value that made the result meaningful. A timestamp represented one way in one part of the system can shift when converted elsewhere. The error looks like arithmetic, but the cause is usually a mismatch between the data's actual range and the range the software assumed would be enough.
The disasters were not caused by one bad decimal
The Patriot missile failure at Dhahran is the example that prevents floating point from remaining pub trivia. On 25 February 1991, a Patriot battery failed to track and intercept an incoming Scud missile. The Scud struck an Army barracks and killed 28 Americans. GAO's 1992 report traced the failure to an inaccurate tracking calculation in the weapons control computer. The inaccuracy grew worse the longer the battery ran, and by the time of the incident it had been operating continuously for more than 100 hours.5
The mechanism was brutally mundane. The Patriot system kept time in tenths of seconds as an integer, then converted time into a real number for the range-gate calculation that predicted where the Scud would appear next. The weapons control computer used 24-bit registers for this calculation, so the conversion lost precision. That loss was proportional to the target's velocity and the system's running time. The longer the battery stayed up, the further the predicted search area shifted from the incoming missile.5
Nobody needed a dramatic hardware explosion to create the problem. A decimal tenth, limited precision, long uptime, and an operational assumption about how long the system would run were enough. The modified software that compensated for the inaccurate time calculation reached Dhahran on 26 February 1991, the day after the attack.5
Ariane 5 Flight 501 is a different kind of numeric failure, which is exactly why it belongs in the same conversation without being lazily labelled as the same bug. On 4 June 1996, the maiden Ariane 5 launch veered off course, broke up, and exploded roughly 40 seconds after the flight sequence began. ESA's inquiry material traced the failure to the loss of guidance and attitude information after both inertial reference systems failed.6
The deeper cause was not that floating-point addition rounded a decimal strangely. The failure involved a value related to horizontal bias that could exceed the representable range expected by software inherited from Ariane 4. The software attempted a conversion into a smaller integer format, raised an exception, shut down the active inertial reference system, and then repeated the failure in the backup system. Diagnostic data then fed into the flight control system as if it were valid flight data.6
That is not a cute story about 0.1 + 0.2. It is a system story: reused software, changed flight profile, insufficiently representative testing, and numeric assumptions that were true enough for one launcher but false for another. The arithmetic was only the place where the system's false assumption became undeniable.
The Pentium FDIV bug adds a third lesson. In 1994, Intel's early Pentium processors produced incorrect results for a small set of floating-point division operations. Contemporary reporting described missing entries in a lookup table used by the processor's division logic, which meant certain inputs produced wrong quotients.7 This was not ordinary rounding error. It was an implementation defect in floating-point hardware. The distinction matters because floating point is often blamed for three different things at once: inherent representation limits, unsafe conversion choices, and actual bugs. They require different fixes.
Choosing the right kind of wrong
The practical lesson is not to avoid floating point. That would be like avoiding knives because surgery can go badly. Floating point is the right tool for enormous areas of computing: graphics, physics engines, machine learning, signal processing, scientific modelling, geospatial work, telemetry, statistics, and anywhere approximate real-number arithmetic is the intended contract.
The mistake is using it without naming the contract.
If a value is a count, use an integer. If a value is money, strongly consider storing the minor unit as an integer or using decimal arithmetic where the language and database support it. If a value is a measurement, decide the tolerance explicitly and compare within that tolerance rather than pretending equality is stable. If a value crosses a boundary between systems, test the conversion at the smallest value, largest value, negative value, zero, just-above-threshold value, and just-below-threshold value. Boundary tests are cheaper than inquiry boards. A bold claim, admittedly.
The standard itself is not the villain. IEEE 754 standardised behaviour that had previously varied across machines, and that standardisation made portable numerical software far more realistic.2 The villain is the habit of treating a convenient approximation as if it were a mathematical guarantee. Floating point gives the nearest representable answer under a defined set of rules. It does not know whether that answer is safe for an invoice, a missile battery, a rocket, or a test assertion someone wrote at 23:40 because the release had to go out before morning.
The useful question is rarely whether floating point is accurate. It is accurate according to its contract. The useful question is whether that contract matches the domain. For a physics simulation, tiny relative error is often the normal cost of modelling reality with finite hardware. For a bank ledger, a one-cent discrepancy is not a modelling artefact; it is a reconciliation problem. For a safety system, a small timing error may be harmless for ten minutes and dangerous after 100 hours. Context decides whether approximation is engineering or negligence.
So the next time 0.1 + 0.2 becomes 0.30000000000000004, do not stop at the joke. Ask what representation the program chose, what domain the value belongs to, how error accumulates, where conversions happen, and which comparisons depend on exactness. The number your computer prints is not wrong in the way beginners think. It is wrong in the way engineering trade-offs are always wrong: deliberately, consistently, and only safe when the people using them understand the bargain.
Choose the bargain on purpose. The maths will not forgive pretending there was no bargain at all.
Footnotes
-
Ecma International. (2022). 'ECMA-262, 13th Edition / June 2022: ECMAScript 2022 Language Specification.' Ecma International. https://tc39.es/ecma262/2022/multipage/ecmascript-data-types-and-values.html#sec-ecmascript-language-types-number-type ↩ ↩2
-
IEEE. (2019). 'IEEE Standard for Floating-Point Arithmetic.' IEEE Std 754-2019. IEEE Standards Association. https://standards.ieee.org/ieee/754/6210/ ↩ ↩2
-
Goldberg, David. (1991). 'What every computer scientist should know about floating-point arithmetic.' ACM Computing Surveys, 23(1), 5-48. Association for Computing Machinery. https://doi.org/10.1145/103162.103163 ↩
-
Varshney, Lav R. (2019). 'The Deadly Consequences of Rounding Errors.' Slate. https://slate.com/technology/2019/10/round-floor-software-errors-stock-market-battlefield.html ↩
-
U.S. General Accounting Office. (1992). 'Patriot Missile Defense: Software Problem Led to System Failure at Dhahran, Saudi Arabia.' GAO/IMTEC-92-26. https://www.gao.gov/assets/imtec-92-26.pdf ↩ ↩2 ↩3
-
European Space Agency. (1996). 'Ariane 501 - Presentation of Inquiry Board report.' ESA Press Release N° 33-1996. https://www.esa.int/Newsroom/Press_Releases/Ariane_501_-_Presentation_of_Inquiry_Board_report ↩ ↩2
-
O'Reilly, Richard. (1994). 'Does Your PC Have the Pentium Bug?' Los Angeles Times. https://www.latimes.com/archives/la-xpm-1994-12-13-fi-8509-story.html ↩
