---
title: "Why Is 0.1 + 0.2 Not 0.3? Floating Point in Python"
description: "Why 0.1 + 0.2 gives 0.30000000000000004: what Python really stores for 0.1, how small errors grow, and five fixes that work."
slug: why-is-0-1-plus-0-2-not-0-3
canonical: https://learn.modernagecoders.com/blog/why-is-0-1-plus-0-2-not-0-3/
date: 2026-09-28
dateModified: 2026-09-28
category: "Programming"
tags: ["Python", "Floating Point", "Computer Science", "Debugging"]
keywords: ["why is 0.1 + 0.2 not equal to 0.3", "floating point error python", "0.1 + 0.2 python", "python compare floats", "python decimal vs float", "math.isclose", "floating point explained"]
readTime: "7 min read"
author: "Modern Age Coders Team"
---
# Why Is 0.1 + 0.2 Not 0.3 in Python?

> What Python really stores for 0.1, why it happens in every language, how the errors grow, and the five fixes, with every output shown.

![A Python terminal showing 0.1 + 0.2 = 0.30000000000000004 and 0.1 + 0.2 == 0.3 is False](/images/blog/why-is-0-1-plus-0-2-not-0-3/00-hero.png)

*By Modern Age Coders Team · 2026-09-28 · 7 min read*

**Quick answer:** 0.1 + 0.2 gives 0.30000000000000004 because computers store numbers in binary, where 1/10 is a never-ending repeating fraction, just like 1/3 in decimal. Python keeps 53 significant binary digits, so 0.1 is stored as a very close approximation, and the small errors in 0.1, 0.2 and 0.3 do not cancel. JavaScript, Java and C behave identically. Compare with math.isclose, round only for display, use Decimal from strings or Fraction for exact values, and keep amounts that must be exact as whole numbers.

Type `0.1 + 0.2` into Python and you get `0.30000000000000004`. Ask whether it equals 0.3 and Python says False. Almost every programmer meets this sooner or later, and the first reaction is usually that something is broken. It is not. It is a direct consequence of how computers store decimal numbers, and once you understand it you will know exactly when it matters and what to do about it.

This guide shows what Python actually stores for 0.1, explains why in terms of a simple analogy with 1/3, shows how small errors grow, and gives the five practical fixes. Every output below is real, including the same test in JavaScript.

## The surprise

**surprise.py**

```python
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
print(f"{0.1:.20f}")
print(f"{0.3:.20f}")
print(f"{0.1 + 0.2:.20f}")
```

**Output**

```text
0.30000000000000004
False
0.10000000000000000555
0.29999999999999998890
0.30000000000000004441
```

Printing more digits shows what is going on. 0.1 is really stored as 0.10000000000000000555..., slightly more than 0.1. 0.3 is stored as 0.29999999999999998890..., slightly less. Their errors point in opposite directions, so 0.1 + 0.2 lands just above 0.3 while the stored 0.3 sits just below it.

This is not a Python quirk. JavaScript gives exactly the same answer:

**In JavaScript**

```javascript
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
```

**Output**

```text
0.30000000000000004
false
```

Java, C, C++ and most other languages do too, because they all follow the same international standard for these numbers, IEEE 754, which is built into the processor itself.

## Why it happens: some fractions never end

Try writing 1/3 as a decimal. You get 0.3333..., and the 3s never stop. However many digits you write, you are always a little short. That is not a failing of arithmetic; it is because 3 does not divide evenly into any power of 10.

Computers store numbers in [binary](/blog/binary-numbers-explained), base 2, and in binary the same thing happens to 1/10. Because 10 contains a factor of 5, which does not divide any power of 2, one tenth becomes a repeating pattern that never ends. Here it is, worked out digit by digit:

**binary.py**

```python
from fractions import Fraction

def binary_digits(x, n):
    """First n binary digits after the point, by repeated doubling."""
    digits = ""
    for _ in range(n):
        x *= 2
        digits += "1" if x >= 1 else "0"
        if x >= 1:
            x -= 1
    return digits

print("1/10 in binary: 0." + binary_digits(Fraction(1, 10), 32) + "...")
print("1/4  in binary: 0." + binary_digits(Fraction(1, 4), 8))
print("1/3  in decimal: 0." + "3" * 20 + "...")
```

**Output**

```text
1/10 in binary: 0.00011001100110011001100110011001...
1/4  in binary: 0.01000000
1/3  in decimal: 0.33333333333333333333...
```

![Some fractions never end: 1/3 in decimal is 0.333 repeating, 1/10 in binary is 0.0001100110011 repeating, while 1/4 in binary is exactly 0.01](/images/blog/why-is-0-1-plus-0-2-not-0-3/01-analogy.png)

*0.1 is to binary what 1/3 is to decimal.*

A standard Python float has room for 53 significant binary digits. It keeps the first 53 and rounds, so what gets stored is the closest value it can hold, not 0.1 itself. Numbers built from halves, quarters and eighths, such as 0.5, 0.25 and 0.375, are stored exactly; most other decimals are not.

## What Python actually stores

You can ask Python for the exact value hiding behind 0.1:

**exact.py**

```python
from decimal import Decimal

print(Decimal(0.1))                 # the exact value Python stores for 0.1
print((0.1).as_integer_ratio())     # the same value as a fraction
print(2 ** 55)
```

**Output**

```text
0.1000000000000000055511151231257827021181583404541015625
(3602879701896397, 36028797018963968)
36028797018963968
```

![The binary digits of 0.1 with the repeating 0011 pattern highlighted; the stored value is the fraction 3602879701896397 over 36028797018963968, which is 2 to the 55, exactly 0.1000000000000000055511151231257827021181583404541015625](/images/blog/why-is-0-1-plus-0-2-not-0-3/02-stored.png)

*Accurate to about 17 significant digits, which is excellent, but not exact.*

The stored value is the fraction 3602879701896397 / 36028797018963968, and that bottom number is 255. Written out as a decimal it is 0.1000000000000000055511151231257827021181583404541015625. That is accurate to about 17 significant digits, which is more than enough for almost everything. It just is not exactly 0.1, and the == operator checks for exact equality.

## Small errors can grow

One tiny error rarely matters. Many of them, added together, can:

**drift.py**

```python
import math

total = 0.0
for i in range(1, 1_000_001):
    total += 0.1
    if i in (10, 1_000, 1_000_000):
        print(f"adding 0.1 {i:>9,} times gives {total!r}")

print("math.fsum of ten 0.1s:", math.fsum([0.1] * 10))
```

**Output**

```text
adding 0.1        10 times gives 0.9999999999999999
adding 0.1     1,000 times gives 99.9999999999986
adding 0.1 1,000,000 times gives 100000.00000133288
math.fsum of ten 0.1s: 1.0
```

![Adding 0.1 repeatedly: 10 times gives 0.9999999999999999, 1,000 times gives 99.9999999999986, 1,000,000 times gives 100000.00000133288](/images/blog/why-is-0-1-plus-0-2-not-0-3/03-drift.png)

*The error grows with the number of additions.*

Ten additions already give 0.9999999999999999, and a million give 100000.00000133288 instead of 100,000. For a game character's position or a physics simulation that runs for hours, this kind of drift is a real problem. Python's `math.fsum` keeps track of the digits that ordinary addition loses, and returns exactly 1.0 for ten 0.1s.

## Five ways to handle it

**fixes.py**

```python
import math
from decimal import Decimal
from fractions import Fraction

a = 0.1 + 0.2
print("isclose:  ", math.isclose(a, 0.3))
print("round:    ", round(a, 2) == 0.3)
print("Decimal:  ", Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))
print("Decimal from floats:", Decimal(0.1) + Decimal(0.2) == Decimal("0.3"))
print("Fraction: ", Fraction(1, 10) + Fraction(2, 10) == Fraction(3, 10))
print("integers: ", 10 + 20 == 30, "(work in tenths, or the smallest unit)")
```

**Output**

```text
isclose:   True
round:     True
Decimal:   True
Decimal from floats: False
Fraction:  True
integers:  True (work in tenths, or the smallest unit)
```

![What to use instead of ==: math.isclose for comparing results, round or formatting for display, Decimal from a string for exact decimal arithmetic, Fraction for exact fractions, whole numbers of the smallest unit for amounts that must be exact](/images/blog/why-is-0-1-plus-0-2-not-0-3/04-fixes.png)

*Pick the fix that matches what you are doing.*

1. **Comparing:** never use == on floats that came from calculations. Use `math.isclose(a, b)`, which allows a tiny tolerance.
2. **Displaying:** round when you show a number, not while you calculate. `f"{x:.2f}"` prints two decimal places.
3. **Exact decimals:** the `decimal` module does arithmetic in base 10. Build values from strings: `Decimal("0.1")`. As the output shows, `Decimal(0.1)` copies the float's error, and the comparison fails.
4. **Exact fractions:** `fractions.Fraction` stores numerator and denominator as whole numbers, so 1/10 + 2/10 is exactly 3/10.
5. **Amounts that must add up exactly:** store them as whole numbers of the smallest unit, and only convert for display. Whole numbers are always exact in Python.

> **Where this causes real bugs**

> Loops that step by 0.1 and test for an exact end value can run one time too many or never stop. Totals of many small amounts can be off by a fraction. Tests that compare floats with == fail on one machine and pass on another. In each case, the fix is one of the five above.

## Is this a problem for beginners?

Mostly no. For everyday calculations, printing a float shows a sensibly rounded value, and 17 significant digits is far more precision than any real measurement. It matters in three situations: comparing results with ==, adding up many small values, and anything that must be exact to the last digit. Knowing why it happens turns a baffling bug into a two-minute fix, and it is a great example of why understanding [how computers store numbers](/blog/binary-numbers-explained) is worth the effort.

> Floats are not broken. They are extremely good approximations, and == asks for perfection.

## How we teach it

Floating point is a good example of a principle on our [how we teach](/how-we-teach) page: mistakes are data, not failures. An unexpected output like 0.30000000000000004 is the start of an investigation, not a reason to give up. Tracing code line by line until every step can be predicted, including what the computer really stores, is how that investigation ends. Our [Python course for teens](/courses/python-complete-masterclass-teens) runs one to one or in small groups of 5 to 10.

[Book a free class](/book-demo) [Book a priority demo](/book-demo)

## Frequently asked questions

**Why is 0.1 + 0.2 not equal to 0.3 in Python?**

Because 0.1, 0.2 and 0.3 cannot be stored exactly in binary, just as 1/3 cannot be written exactly in decimal. Each is stored as the nearest possible value, and the tiny errors make 0.1 + 0.2 come out as 0.30000000000000004, which is not equal to the stored 0.3.

**Is this a bug in Python?**

No. It comes from the IEEE 754 standard for floating-point numbers, which is built into computer processors. JavaScript, Java, C and most other languages give exactly the same result.

**How do I compare floats in Python?**

Use math.isclose(a, b) instead of ==. It checks whether the numbers are equal within a small tolerance, which is the right question for values produced by calculations.

**How do I get exact decimal arithmetic in Python?**

Use the decimal module and create values from strings, such as Decimal('0.1'). Creating them from floats, as in Decimal(0.1), copies the float's small error.

**Which decimals can a computer store exactly?**

Those that can be written as a whole number divided by a power of 2, such as 0.5, 0.25, 0.75 and 0.375. Most other decimals, including 0.1, 0.2 and 0.3, are stored as close approximations.

**Does rounding fix floating point errors?**

Rounding is right for displaying results, for example with round(x, 2) or an f-string. It is not a good way to make calculations exact; for that, use Decimal, Fraction or whole numbers.

**How accurate are Python floats?**

A standard Python float stores 53 significant binary digits, about 15 to 17 significant decimal digits. That is far more precise than almost any real-world measurement.

---

*Source: https://learn.modernagecoders.com/blog/why-is-0-1-plus-0-2-not-0-3/*
