---
title: "4.5 Floating-point types"
description: "Rust's f32 and f64: scientific notation, precision limits, rounding error, infinity, and NaN."
url: "https://learnrust.net/chapter-4/floating-point-types/"
last_updated: "2026-06-11"
---

# 4.5 Floating-point types

Integers can't hold 98.6, half a pizza, or anything astronomical. For fractional and very-large-or-small values, Rust has two **floating-point** types: `f32` (4 bytes) and `f64` (8 bytes), both following the IEEE 754 standard your processor implements in hardware, both always signed.

The name "floating point" describes the trick: the decimal point floats, so the same machinery stores 0.000000012 and 12,000,000,000 by tracking the digits and the point's position separately, like scientific notation. Rust literals can use scientific notation directly, `e` meaning "times ten to the":

```rust
fn main() {
    let big = 1.2e4;
    let tiny = 9.1093837e-31;
    println!("{big} {tiny}");
}
```

```
12000 0.00000000000000000000000000000091093837
```

(That second one is the electron's mass in kilograms, which is the kind of value these types exist for.)

A float literal needs a decimal point or an exponent (`5.0`, not `5`, which is an integer literal), and defaults to `f64`. Choosing between the two types is mercifully one-sided:

> **Best practice**
>
> Use `f64`. It's the default for float literals, modern hardware runs it at full speed, and its extra precision (about 15–16 significant decimal digits, versus about 7 for `f32`) prevents real problems. `f32` is for when something external demands it: graphics APIs, giant arrays where memory halves matter, embedded targets.

## The honesty problem

Now the part of the lesson that will save you a confused afternoon someday. Floating-point types store *approximations*. They have finite digits (those ~7 and ~15-16 figures above), and worse, they think in binary, and most decimal fractions can't be written exactly in binary, the same way 1/3 can't be written exactly in decimal. The value 0.1 in an `f64` is really a number *extremely close to* 0.1, as printing with forced precision reveals:

```rust
fn main() {
    println!("{:.17}", 0.1);
    println!("{}", 0.1 + 0.2);
}
```

```
0.10000000000000001
0.30000000000000004
```

(`{:.17}` requests 17 digits after the point; the formatting machinery gets proper coverage in lesson 5.5.) That second line is the most famous wart in all of computing, and it isn't a Rust bug: it's 0.1 + 0.2 computed faithfully on approximations of 0.1 and 0.2, in every IEEE 754 language on earth. Normal printing rounds kindly, so the wart hides until arithmetic accumulates it into view:

```rust
fn main() {
    let sum = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1;
    println!("{sum}");
    println!("{}", sum == 1.0);
}
```

```
0.9999999999999999
false
```

Ten dimes don't make a dollar, says the computer, missing by one part in ten quadrillion.

> **Warning**
>
> Never compare floating-point values with `==`. Two computations that are mathematically equal routinely differ in their final bits, as above, so equality tests on floats are coin flips weighted against you. The professional technique (compare the *difference* against a tolerance) is in lesson 6.4; until then, the discipline is just: don't.

> **Key insight**
>
> Floating-point rounding is not an occasional malfunction; it's the constant, designed behavior of types that trade exactness for range. The right mental model: an `f64` gives you 15-ish trustworthy digits, and arithmetic slowly spends them. For human-money sums and modest science, that budget is enormous. Just spend it knowingly, and (chapter 18 will repeat this) never store currency in floats when exactness is legally interesting.

## The edge values

Integer division by zero panics (lesson [3.1](https://learnrust.net/chapter-3/syntax-and-semantic-errors/)). Floats, following IEEE 754, do something stranger: they have special values for the occasion, and no panic at all.

```rust
fn main() {
    println!("{}", 5.0 / 0.0);
    println!("{}", -5.0 / 0.0);
    println!("{}", 0.0 / 0.0);
}
```

```
inf
-inf
NaN
```

Positive and negative **infinity** behave about how you'd hope (anything finite is less than `inf`). **NaN**, "not a number," is the designated result for nonsense like 0/0, and it's contagious (arithmetic with NaN yields NaN) and famously antisocial:

```rust
fn main() {
    let nan = f64::NAN;
    println!("{}", nan == nan);
}
```

```
false
```

NaN is the only value in Rust that isn't equal to itself, which is IEEE 754's way of saying "this isn't a value, stop comparing it" (there's a `is_nan()` method for asking properly). You'll meet NaN rarely, but when you do, knowing its handshake saves an hour of disbelief.

## Quiz time

**Question #1**

What does this print, and why doesn't the second line print `3.5`?

```rust
fn main() {
    println!("{}", 7.0 / 2.0);
    println!("{}", 7 / 2);
}
```

<details class="solution">
<summary>Show solution</summary>

```
3.5
3
```

Float division keeps the fraction; integer division (two integer operands) discards it, keeping only the whole part. Same symbol, two operations, chosen by the operand types (the full story is lesson 6.2).

</details>

**Question #2**

A colleague's loop adds 0.01 to a total ten thousand times and then checks `total == 100.0`, which fails. Diagnose, in one sentence each: what happened, and what should the check become?

<details class="solution">
<summary>Show solution</summary>

Each 0.01 is a binary approximation, and ten thousand additions accumulated the tiny errors, so `total` is extremely close to, but not exactly, 100.0. The check should test whether the difference is smaller than a tolerance (lesson 6.4's technique), not exact equality.

</details>

**Question #3**

True or false: `f64::NAN == f64::NAN`. And how *should* you test whether a value is NaN?

<details class="solution">
<summary>Show solution</summary>

False (uniquely, deliberately false). Use the method built for it: `value.is_nan()`.

</details>

From numbers to the smallest type of all: two values, one byte, and most of the decision-making in every program you'll ever write.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
