Question 8 of 20 Fundamentals Concept
Why does 0.1 + 0.1 + 0.1 not equal 0.3 in Python, and how should you compare floats?
Short answer
Most decimal fractions have no exact binary representation, so each float is a close approximation and small errors accumulate. Compare floats with a tolerance, using math.isclose or numpy.isclose with tolerances chosen for the data, never with ==.
Why
A float stores a binary fraction. One tenth is an infinitely repeating fraction in base 2, so Python stores the nearest value it can, which is very slightly off. Add three of them and the tiny errors no longer cancel, so the sum is not the same float as the literal 0.3.
The fix is to ask whether two numbers are close enough for the purpose. math.isclose uses a relative tolerance of 1e-09 by default and an absolute tolerance of 0.0, which means comparing anything to zero always fails unless you pass abs_tol. numpy.isclose defaults to rtol 1e-05 and atol 1e-08, and its own documentation warns that this atol is wrong for values much smaller than one. With those defaults isclose(1e-9, 2e-9) is True, which would hide a twofold difference in a concentration measured in molar units. The formula in numpy.isclose is also asymmetric, because it treats the second argument as the reference.
In tests, numpy.testing.assert_allclose is stricter, with rtol 1e-07 and atol 0. When exact decimal arithmetic is genuinely required, as for money, the decimal module is the tool, but scientific measurements are rarely exact to begin with, so a tolerance tied to measurement precision is usually the honest choice.
A strong answer covers
- Decimal fractions such as 0.1 are approximated in binary
- Rounding errors accumulate across operations
- Use a relative and an absolute tolerance, not ==
- Default absolute tolerances can be wrong for very small values
- Choose tolerances from measurement precision
Common mistakes
- Rounding both sides to a few decimals and comparing with ==, which still fails near rounding boundaries
- Comparing a value to zero with math.isclose and no abs_tol, which is False for every nonzero input
- Trusting numpy.isclose defaults for quantities around 1e-9 such as molar concentrations
The interviewer might ask next
How would you choose rtol and atol for a regression test on a normalized expression matrix?