Why is 0.1 + 0.2 not exactly 0.3?
One tenth cannot be written exactly in binary, any more than one third can be written exactly in decimal — so the computer stores a very close approximation and the small errors show up when you add them.
Simple intuition
The plain reason, in everyday words
Try writing one third as a decimal. You get 0.3333… and it never ends, so if you have to stop somewhere you have written down something slightly smaller than a third. Nothing is wrong with your arithmetic; the number simply cannot be expressed exactly in that notation. Computers have the same problem, but in base two, and the fractions that trouble them are different from the ones that trouble us. One tenth, which looks perfectly tidy written as 0.1, becomes an endlessly repeating pattern in binary. So the computer stores the nearest value it can, which is very slightly off. Do that for 0.1 and again for 0.2, add the two approximations, and the result is a number extremely close to 0.3 but not identical to it. Print enough digits and you can see it: 0.30000000000000004.
It is a bug in the language, and some other language gets it right.
Almost every language uses the same IEEE 754 hardware format and produces the same answer. Languages that appear to get it right are formatting the output to fewer digits, not storing anything different.
The computer made a mistake during the addition.
The addition is exact to the last bit permitted by the format. The discrepancy is already present in the stored values of 0.1 and 0.2 before any arithmetic runs.
Using more precision fixes it.
More bits move the error further down but never remove it, because one tenth has no finite binary expansion at any precision. What fixes it is changing base — a decimal type — or using integers.
Floating point is inherently imprecise and unreliable.
It is exactly specified and deterministic, with guaranteed correctly rounded basic operations. The pitfalls come from expecting decimal behaviour from a binary format, not from randomness.
It is the single most common surprise in programming, and the explanation generalises: any finite representation of numbers has values it cannot express, and choosing the representation is choosing which values those are. Knowing that gives you the two rules that prevent most real damage — money is not a float, and floats are not compared for equality — and it explains a whole family of bugs that otherwise look like the machine behaving arbitrarily.
Who worked it out
Early computers each had their own floating-point formats with different ranges, rounding behaviour and handling of edge cases, so the same program could give different answers on different machines.
What problem forced it
William Kahan led the effort to standardise, and IEEE 754 was published in 1985, specifying formats, rounding modes and the treatment of infinities and not-a-number. Kahan received the Turing Award in 1989 for the work.
How it changed since
The standard was adopted almost universally in hardware, which made numerical results portable. A 2008 revision added decimal floating-point formats aimed precisely at the financial use case where binary fractions are the wrong tool.
How to handle money in code correctly
Integer minor units or a decimal type, and the rounding rules that go with each.
Why summing a list in a different order changes the answer
Floating-point addition is not associative, which matters for parallel code and reproducibility.
Written for Curio rather than collected from a forum — it is part of the curated corpus that ships with the platform. The references it draws on are listed under Sources.