#304 Aug 7, 2026

304. trunc & fract — Split a Float Without the Cast Round-Trip

Need the whole and fractional parts of a float? The (x as i64) as f64 round-trip works — until the value doesn’t fit in an i64. trunc and fract split the float directly.

The cast detour everyone writes first:

1
2
3
4
5
6
let t = 7.75_f64;

let whole = (t as i64) as f64;
let frac = t - whole;
assert_eq!(whole, 7.0);
assert_eq!(frac, 0.75);

The direct version — no casts, no subtraction:

1
2
3
let t = 7.75_f64;
assert_eq!(t.trunc(), 7.0);
assert_eq!(t.fract(), 0.75);

Both parts keep the sign, and trunc() + fract() always reassembles the original — which also makes trunc the “toward zero” sibling in the rounding family from bite 303:

1
2
3
4
let t = -7.75_f64;
assert_eq!(t.trunc(), -7.0);
assert_eq!(t.fract(), -0.75);
assert_eq!(t.trunc() + t.fract(), t);

The real reason to drop the cast: as i64 silently saturates past ±2⁶³, so the round-trip hands back the wrong number without a peep. trunc has no range limit:

1
2
3
4
5
let big = 1e19_f64; // > i64::MAX

assert_eq!(big.trunc(), 1e19); // correct
assert_eq!((big as i64) as f64,
    9.223372036854776e18); // wrong, silently

The classic use case — splitting a quantity across two units:

1
2
3
4
5
let secs = 92.375_f64;
let mins = (secs / 60.0).trunc();
let rest = secs - mins * 60.0;
assert_eq!(mins, 1.0);
assert_eq!(rest, 32.375);

Note that fract on a negative number is negative — if you want “distance above the floor” instead (always in [0, 1)), that’s x - x.floor(), or x.rem_euclid(1.0).

← Previous 303. round_ties_even — round() Pushes Every Half Up, and Your Totals Drift Next → 305. to_bits — Hash and Dedup Floats Without a Wrapper Crate