In Python, `0.1 + 0.2 == 0.3` evaluates to `False`, while `0.1 + 0.1 == 0.2` evaluates to `True`. Even more confusingly, `sum([0.1] * 10000) == 1000` may evaluate differently depending on the Python version. I understand that 0.1 cannot be represented exactly in binary IEEE 754 floating-point format, but I expected repeatedly adding it to make the error grow predictably. Why do these expressions behave differently, and what is the correct way to compare floating-point values?
3 Answers
The stored value for `0.1` is only an approximation, roughly `0.10000000000000000555`. When two floating-point numbers are added, the result is rounded again to the nearest representable value. Sometimes those approximations and rounding steps happen to produce exactly the same stored value as another literal. That is why `0.1 + 0.1` can compare equal to `0.2`, while `0.1 + 0.2` can end up one tiny representable step away from `0.3`. The errors do not necessarily grow in a simple linear pattern; they can partially cancel or be rounded in different directions.
For calculations where a small error is acceptable, do not normally use direct equality. Compare with a tolerance instead, such as `math.isclose(x, 0.3, rel_tol=1e-9, abs_tol=1e-12)`. A fixed epsilon can work for values in a known range, but relative tolerance is important when numbers may be very large or very small. If you need exact decimal arithmetic, such as for currency, use integers representing the smallest unit or Python's `decimal.Decimal` rather than binary floats.
Repeated addition usually does accumulate error, for example `total = 0.0; for _ in range(10000): total += 0.1` may not compare equal to `1000`. However, Python's `sum()` implementation has changed in newer versions and can use a more accurate summation algorithm that keeps track of rounding error better. Consequently, `sum([0.1] * 10000)` can produce a result that compares equal to `1000` in some versions but not others. That does not mean `0.1` is represented exactly; it means the summation algorithm produced a rounded result equal to the stored representation of `1000.0`.
Printing every intermediate value with many decimal places can help demonstrate the behavior, but the displayed decimal is still just a formatting of the underlying binary value. The exact result also depends on the order and method of summation.

This is not specific to Python. Binary floating point has a finite number of bits, so many ordinary decimal fractions cannot be represented exactly. The same general behavior appears in other languages that use IEEE 754 floating-point numbers.