The short answer
Type 0.1 + 0.2 into almost any programming language and you get 0.30000000000000004. It is not a bug in your language.
Quick answer: Computers store most fractional numbers in binary floating point, following the IEEE 754 standard. Just as 1/3 cannot be written exactly in decimal (0.3333...), the values 0.1 and 0.2 cannot be written exactly in binary. The computer stores the nearest values it can represent, which are very slightly off. Adding them produces a result that is very slightly different from the nearest representation of 0.3, and when the language prints it, the difference shows.
The site 0.30000000000000004.com lists how dozens of languages display this same sum.
The decimal analogy
In base 10, a fraction has a finite expansion only if its denominator is built from the prime factors of 10, which are 2 and 5. So 1/2, 1/4 and 1/5 are fine, but 1/3 is 0.333... forever.
Binary is base 2, so only fractions whose denominator is a power of 2 terminate:
| Fraction | Decimal | Binary |
|---|---|---|
| 1/2 | 0.5 | 0.1 |
| 1/4 | 0.25 | 0.01 |
| 1/8 | 0.125 | 0.001 |
| 1/10 | 0.1 | 0.000110011001100110011... (repeats forever) |
One tenth is a repeating fraction in binary. With a finite number of bits, it has to be cut off and rounded.
How a float is stored
The most common type, a 64-bit double, has three parts, much like scientific notation:
| Part | Bits | Purpose |
|---|---|---|
| Sign | 1 | Positive or negative |
| Exponent | 11 | Where the binary point goes (the scale) |
| Significand (mantissa) | 52 | The significant digits |
That gives roughly 15 to 17 significant decimal digits of precision. A 32-bit float has about 7.
What the computer actually stores for 0.1 is:
0.1000000000000000055511151231257827021181583404541015625
It is the closest representable value, and most languages print it as 0.1 because that is the shortest decimal that identifies it. As the Python documentation puts it, this is in the nature of binary floating point, not a fault of any particular language.
Where the extra 4 comes from
- Stored 0.1 is slightly above one tenth.
- Stored 0.2 is slightly above two tenths.
- Their exact sum falls between two representable values and is rounded to the nearer one.
- That value happens to be the next representable number above the one closest to 0.3.
So the sum and the literal 0.3 are two different, adjacent doubles. Printing enough digits to tell them apart gives 0.30000000000000004.
Other surprises from the same cause
>>> 0.1 + 0.2 == 0.3
False
>>> sum([0.1] * 10) == 1.0
False
>>> 9007199254740993.0
9007199254740992.0
>>> 0.1 + 0.2 + 0.3 == 0.3 + 0.2 + 0.1
False
- Equality fails. Two calculations that should match can differ in the last bit.
- Errors accumulate. Each operation may add a tiny rounding error.
- Large integers lose precision. Above 2^53, a double cannot represent every whole number. JavaScript's
Number.MAX_SAFE_INTEGERmarks that limit. This is why large database IDs sent as JSON numbers can be silently corrupted, and are often sent as strings. - Order matters. Floating point addition is not associative.
There are also special values:
- Infinity and -Infinity from overflow or dividing by zero.
- NaN ("not a number") from invalid operations like
0/0. NaN is not equal to anything, including itself. - Negative zero, which equals zero but has a sign bit.
How to handle it
Compare with a tolerance
Never test floats for exact equality after arithmetic. Check that they are close enough:
import math
math.isclose(0.1 + 0.2, 0.3) # True
Math.abs(a - b) < Number.EPSILON * Math.max(1, Math.abs(a), Math.abs(b))
Use integers for money
Never store currency in a float. Either store the smallest unit as an integer (cents, pence), or use a decimal type:
| Language | Exact decimal option |
|---|---|
| Python | decimal.Decimal |
| Java | BigDecimal |
| C# | decimal |
| JavaScript | Integers in minor units, BigInt, or a decimal library |
| SQL | DECIMAL / NUMERIC |
Small rounding errors in financial code add up to real discrepancies. Payment systems have enough ways to go wrong already; see how payment systems avoid charging you twice.
Round when displaying, not when storing
Keep full precision in calculations and format to the needed number of decimal places only for output.
Be careful summing many values
When adding many numbers of very different sizes, small ones can be lost. Sorting from smallest to largest, or using a compensated algorithm such as Kahan summation (Python's math.fsum), helps.
Why not just use decimal everywhere?
Binary floating point is implemented directly in CPU hardware, so it is extremely fast and uses a fixed, small amount of memory. It covers an enormous range, from subatomic scales to astronomical ones. For physics, graphics, statistics and machine learning, a relative error around one part in ten quadrillion is irrelevant.
Decimal types are exact for decimal fractions but slower, and they still cannot represent 1/3 exactly. No finite format can represent every real number. The right question is which errors you can tolerate.
Floating point belongs to a family of topics where the computer's representation does not quite match human expectations, alongside text encoding and dates and time zones.
Frequently asked questions
Is this a bug in JavaScript or Python?
No. Every language using IEEE 754 doubles gives the same result. Some hide it by rounding when printing.
How do I make 0.1 + 0.2 equal 0.3?
Use a decimal type, work in integers (1 + 2 tenths), or compare with a tolerance instead of ==.
Is a double more accurate than a float?
Yes. A double has about 15 to 17 significant decimal digits; a 32-bit float has about 7. Both have the same kind of rounding behaviour.
Why does NaN not equal itself?
The standard defines it that way so that invalid results never accidentally compare as equal. Use isNaN or math.isnan to test for it.
Conclusion
Floating point numbers are binary approximations with a fixed number of digits. Most decimal fractions cannot be stored exactly, so tiny errors are normal and expected. Compare with a tolerance, keep money in integers or decimals, and format only at the edges, and floating point will do exactly what it was designed to do.
Related articles
- Why Unicode Exists (and Why Emojis Break Things)
- How a Compiler Turns Your Code Into Machine Instructions
- How Payment Systems Avoid Charging You Twice (Idempotency)
- Why Time Zones Are a Programmer's Nightmare
