Why does 0.1 + 0.2 == 0.3 evaluate to False in Python?
answer
- The literal is not the number
- Base two cannot write one tenth
- Powers of two only
- 53 bits of significand
- Stored 0.1 is a hair too big
basics
~20 sPython's float is IEEE-754 binary64, which stores exactly only fractions with a power-of-two denominator. 0.1, 0.2 and 0.3 each become nearby approximations, and the sum of the first two lands one step above the stored 0.3.
solid answer
~40 sA Python `float` is an IEEE-754 binary64 double: 53 bits of significand, so the only values it stores exactly are of the form m/2**k. One tenth is not such a fraction, so `0.1` is stored as 0.1000000000000000055511151231257827..., `0.2` as a similar approximation, and `0.3` as a value slightly *below* three tenths. Adding the first two doubles and rounding the result gives 0.30000000000000004 — a different double from the one the literal `0.3` produces, so `==` is False. This is not a Python bug; it is how binary floating point behaves on any language running on the same hardware. The practical rule is that `==` is the wrong tool for a *computed* float: compare with `math.isclose`, or use an exact type when the decimal digits themselves are the contract, as with money.
code
pycon · 11 lines>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
>>> from decimal import Decimal
>>> Decimal(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> (0.1).hex()
'0x1.999999999999ap-4'
>>> 0.5 + 0.25 == 0.75
Truego deeper
Be ready to say the phrase 'binary floating point' and give the one-line reason: 0.1 has no exact base-two form. Then state the practical rule you follow, which is to compare computed floats with a tolerance rather than with ==.
An interviewer expects the mechanics: 53 bits of significand, only m/2**k is exact, each literal rounds on the way in, and the printed value is the shortest round-tripping repr rather than the stored bits. Be able to show the exact value.
Show where this bites in running systems: totals that drift over a long accumulation, thresholds that flip, and reconciliation reports that disagree by a cent. Explain when you reach for compensated summation and when you switch the whole quantity to integers or an exact decimal type.
Own the policy question. Decide once, for a codebase, which quantities are allowed to be floats at all, where the boundary of exactness sits between the database column, the serialization format and the in-process type, and how that choice is enforced in review rather than rediscovered per bug.
### What a `float` actually stores CPython's `float` is a C `double`, an IEEE-754 **binary64** value: one sign bit, an 11-bit exponent, and 52 stored significand bits plus an implicit leading 1, for 53 bits of precision. That layout can represent exactly one family of real numbers — **dyadic rationals**, values of the form `m / 2**k` within range. `0.5`, `0.25`, `0.75`, `2.5` and every integer up to 2**53 are in that family and behave with perfect arithmetic. One tenth is not: its denominator has a factor of five, so in base two it is the repeating expansion 0.0001100110011... , infinitely long and therefore impossible to store. When the compiler sees the literal `0.1` it rounds that infinite expansion to the nearest binary64 value, which is ``` 0.1000000000000000055511151231257827021181583404541015625 ``` — very slightly *more* than one tenth. `0.2` gets a similar upward error, while the literal `0.3` rounds to a value slightly *less* than three tenths. Adding the stored 0.1 and the stored 0.2 gives an exact mathematical sum that is itself rounded to the nearest double, and that double is one unit in the last place above the double the literal `0.3` produced. Two different bit patterns, so `==` says False, correctly. ### Why you rarely notice Since Python 3.1 `repr()` prints the **shortest decimal string that round-trips** back to the same double, so `repr(0.1)` is just `'0.1'` and `str()` matches it. The display hides the error; arithmetic reveals it. Three tools show the truth without arithmetic: `decimal.Decimal(0.1)` prints the full exact value of the stored double, `float.hex` gives the bit-level form (`(0.1).hex()` is `'0x1.999999999999ap-4'`), and `float.as_integer_ratio` returns the exact fraction the double stands for. ### Which errors accumulate Each individual operation is *correctly rounded* — the result is the closest double to the true answer, with at most half an ulp of error. The trouble is repetition. A plain `+=` loop adding `0.1` ten times ends on 0.9999999999999999, because ten small errors do not cancel. Values of wildly different magnitude are worse: adding a tiny number to a huge one can lose the tiny one entirely. `math.fsum` fixes this by tracking exact partial sums and rounding once at the end, and since **Python 3.12** the builtin `sum` uses Neumaier compensated summation for floats, so `sum([0.1] * 10) == 1.0` is now True on 3.12 through 3.14 while the equivalent hand-written loop still is not. ### What to do instead Three responses, in order of how often they are right: 1. **Compare with a tolerance.** `math.isclose(0.1 + 0.2, 0.3)` is True. Any measured or computed quantity — a total, an average, a physics step — should be compared this way rather than with `==`. 2. **Use an exact type when the decimals *are* the contract.** Money, tax and invoice arithmetic want an exact decimal type, or plain `int` counting in the smallest unit (cents, grams, milliseconds), which keeps arbitrary-precision integer arithmetic and never rounds at all. 3. **Keep `==` where it is genuinely safe.** Comparing a float against a value you assigned yourself, or against an integer-valued float below 2**53, is exact and fine. `1.0 == 1` is True, and `(0.5 + 0.25) == 0.75` is True, because every value involved is dyadic. ### What the interviewer is listening for The weak answer is "floats are inaccurate" or "Python has a rounding bug". The strong answer names binary64, explains that decimal fractions with factors other than two cannot be represented, notes that the printed form is a shortest round-trip repr rather than the stored value, and finishes with the practical rule: tolerance comparison for computed values, exact types for money. Mentioning that the same expression is False in essentially every language using IEEE-754 doubles shows you understand it as a property of the format, not of Python.
- If the stored value is not exactly 0.1, why does printing it show `0.1`?Since Python 3.1, `repr` for floats prints the shortest decimal string that reads back as the same double, and `str` matches it. `0.1` is the shortest string that round-trips to that bit pattern, so it is what you see. `decimal.Decimal(0.1)`, `float.hex` or `float.as_integer_ratio` expose the exact stored value.
- Are there decimal literals a Python float does hold exactly?Yes — any value of the form m/2**k in range. `0.5`, `0.25`, `0.125`, `2.75` and every integer up to 2**53 are exact, so `0.5 + 0.25 == 0.75` is True. Only fractions whose denominator has a prime factor other than two, such as tenths, fifths and thirds, must be approximated.
- Is comparing two floats with `==` ever the right thing to do?Yes, when both sides are values you stored rather than computed — a sentinel, a flag, a literal you assigned — or when both are integer-valued and below 2**53. `==` on floats is a well-defined bit comparison; the mistake is using it on the *result of arithmetic*, where each operation may have rounded.
Base ten cannot write one third exactly — 0.3333... never terminates. Binary has the same problem with one tenth; the computer simply stops after 53 bits and stores the nearest value it can.
saying these in an interview costs you the question
- Says Python floats are broken or buggy
- Claims the discrepancy comes from print rounding
- Thinks more decimal places would make 0.1 exact
- Fixes every comparison by rounding both sides
- Believes the problem is unique to Python
- Uses float for currency without hesitation