#309 Aug 10, 2026

309. classify — x > 0.0 Passed, Then 1.0 / x Returned inf

Between zero and the smallest “real” float lives a twilight zone of subnormals — values that pass your > 0.0 guard but blow up the moment you divide by them.

f64::MIN_POSITIVE (~2.2e-308) is the smallest normal float. Below it, floats go subnormal: the exponent is maxed out, so the mantissa gives up leading bits and precision degrades. They’re not zero, but they don’t behave like the floats you know:

1
2
3
4
5
6
7
8
let x = 1e-310_f64; // fell out of some computation

// your guard passes...
assert!(x > 0.0);
assert!(x.is_finite());

// ...but the reciprocal overflows
assert!((1.0 / x).is_infinite());

is_normal is the one-line fix — it’s true only for regular floats, and false for zero, subnormals, infinities, and NaN:

1
2
3
assert!(!x.is_normal());
assert!(x.is_subnormal());
assert!(1.0_f64.is_normal());

When you need to know which weird case you got, classify turns the whole is_nan / is_infinite / == 0.0 ladder into one exhaustive match:

1
2
3
4
5
6
7
8
use std::num::FpCategory;

match x.classify() {
    FpCategory::Normal => { /* safe to invert */ }
    FpCategory::Zero | FpCategory::Subnormal => { /* underflow */ }
    FpCategory::Infinite | FpCategory::Nan => { /* bad input */ }
}
assert_eq!(x.classify(), FpCategory::Subnormal);

The match is exhaustive, so unlike the boolean ladder, the compiler makes sure you handled every category — including the one you forgot exists.

If a division’s denominator came from data instead of a literal, denom.is_normal() is the guard you meant when you wrote denom != 0.0.

← Previous 308. sin_cos — One Angle, Both Halves, No Swap Bug Next → 310. f64::max — The Fold That Quietly Swallows NaN