The fraction 0.625 looks decimal, and it is: six tenths plus two hundredths plus five thousandths. Computers do not store it that way. They store floating-point numbers as base 2, binary, fractions. The decimal 0.625 happens to equal one half plus one eighth, so it fits neatly into binary. Most decimal fractions do not fit at all, and 0.1 is the famous one that refuses.
In base 10, one third is 0.3333…, and no matter how many digits you write, you never reach exactly one third. In base 2, one tenth is the same kind of thing: an infinitely repeating fraction, 0.0001100110011…, and no finite number of bits reaches it exactly. The machine stops at the nearest representable binary fraction and stores that instead.
The Python tutorial shows why this stays hidden. If Python printed the full decimal value it actually stores for 0.1, you would see 0.1000000000000000055511151231257827… with many more digits after it. That is unhelpful, so Python prints a rounded value instead: 0.1. The display looks exact. The stored value is only close.
The illusion breaks the moment you do arithmetic. Three copies of the approximation do not add to the approximation of three tenths:
0.1 + 0.1 + 0.1 == 0.3
# False
The tutorial is explicit that this behavior is inherent to binary floating point, present in Python and in every language that uses the hardware’s floating-point arithmetic, even if some hide the difference in their default output.
The right tool depends on what you are comparing. math.isclose decides whether two inexact values are near enough, and it returns True for that sum against 0.3. But isclose is a tolerance check with two knobs that mean specific things. Its default relative tolerance is 1e-09, meaning the two values agree to about nine decimal digits. The absolute tolerance defaults to zero and becomes the deciding factor when you compare against zero itself. Under those defaults, with a relative tolerance below 1 and an absolute tolerance of zero, isclose(x, 0) is False for any nonzero x, because the allowed difference is measured relative to the larger absolute value, which is abs(x). To test whether a value is effectively zero you must pass a positive absolute tolerance, as in abs_tol=1e-12. The tolerance has to fit the comparison you actually intend. isclose does not make the arithmetic exact, and it is not a promise that every numerical comparison will now behave.
For exact decimal representation, Python offers the decimal module, which holds values in a form that can represent decimal fractions exactly. The fractions module takes a different route, storing exact rationals as a numerator over a denominator. For most work, binary floats are fine. The lesson is not to avoid them. The lesson is to know that a printed float is a rounded picture of a stored approximation, and to compare with that in mind.

The Campfire
No commentsNobody has pulled up a log by this one yet. Be the first to say what you make of it.
Held for the desk. It appears after a look.