# Floating Point: Why 0.1 + 0.2 Isn't 0.3

> Every language prints 0.30000000000000004 and every newcomer files a bug report. It's not a bug — it's binary fractions doing exactly what the IEEE 754 standard says. Here's the why, and what to do about it.

URL: https://harshith.in/blog/floating-point-explained/
Author: Harshith Sunku (Staff Engineer) — https://harshith.in/about/
Tags: fundamentals, systems

Type `0.1 + 0.2` into almost any language and you get `0.30000000000000004`.
This is the most reported non-bug in computing. The floating-point unit is working
perfectly; the problem is that you asked it to store a number it physically
cannot represent.

## Computers Count in Binary Fractions

A float stores numbers as binary fractions — sums of powers of two:

```text
   0.5    = 1/2           = 0.1   in binary
   0.75   = 1/2 + 1/4     = 0.11  in binary
   0.625  = 1/2 + 1/8     = 0.101 in binary
```

Any value that's a sum of powers of two fits exactly. The trouble: **0.1 is not**.
In binary it's a repeating fraction, like 1/3 is in decimal:

```text
   0.1 (decimal) = 0.0001100110011001100110011… (binary, forever)
```

The hardware has finite bits (53 of significand in a 64-bit double), so it stores
the closest representable value — slightly off. Add two slightly-off numbers and
the errors surface in the last digits.

## IEEE 754 in One Picture

A double splits its 64 bits like scientific notation in binary:

```text
   ┌─┬───────────┬────────────────────────────────────┐
   │S│ exponent  │            significand             │
   │1│   11 bits │              52 bits               │
   └─┴───────────┴────────────────────────────────────┘
    sign  scale (×2^e)     the fractional digits

   value = (-1)^S × 1.significand × 2^(exponent − bias)
```

Roughly 15–17 significant decimal digits of precision. Plenty — until you assume
it's exact.

## The Rules That Follow

**Never test floats for equality.** `0.1 + 0.2 == 0.3` is `false`. Compare within
a tolerance instead:

```c
if (fabs(a - b) < 1e-9)   // "close enough", not exact
```

(Real code should scale the tolerance to the magnitude of the values — an absolute
epsilon is wrong for very large or very small numbers.)

**Precision degrades with magnitude.** The 53 bits are relative, so big numbers
have coarse spacing. Past 2^53, integers themselves stop being representable —
`9007199254740993.0` rounds to an even neighbor.

**Don't use floats for money.** Repeated rounding accumulates. Use integer cents,
or a decimal type that represents base-10 fractions exactly.

## When You Need Exactness

- **Money / accounting** → integers (cents) or a decimal library.
- **Comparisons** → tolerance-based, scaled to magnitude.
- **Exact rationals** → a big-rational type that stores numerator/denominator.

Floats are a deliberate trade: enormous range and speed in exchange for exactness.
For physics and graphics that's perfect; for a ledger it's a liability.

## Takeaways

- Floats store binary fractions; decimals like 0.1 don't fit, so they're rounded
  to the nearest representable value.
- IEEE 754 gives ~15–17 significant digits — relative precision, so large numbers
  are spaced coarsely (integers break past 2^53).
- Never compare floats with `==`; use a magnitude-scaled tolerance.
- Use integers or a decimal type for money — never binary floats.
