#303 Aug 7, 2026

303. round_ties_even — round() Pushes Every Half Up, and Your Totals Drift

round() sends every .5 away from zero — so summing rounded values drifts upward. round_ties_even rounds ties to the even neighbor and the bias cancels out.

Round a batch of exact midpoints with round() and watch the total inflate:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let vals = [0.5_f64, 1.5, 2.5, 3.5];
// true sum: 8.0

let away: f64 = vals.iter()
    .map(|v| v.round())
    .sum();
assert_eq!(away, 10.0); // 1 + 2 + 3 + 4

let even: f64 = vals.iter()
    .map(|v| v.round_ties_even())
    .sum();
assert_eq!(even, 8.0); // 0 + 2 + 2 + 4

round() uses “half away from zero”: every tie moves in the same direction, so the errors all point the same way and accumulate. round_ties_even — banker’s rounding — sends ties to whichever neighbor is even, so roughly half go down and half go up:

1
2
3
4
assert_eq!(0.5_f64.round_ties_even(), 0.0);
assert_eq!(1.5_f64.round_ties_even(), 2.0);
assert_eq!(2.5_f64.round_ties_even(), 2.0);
assert_eq!((-2.5_f64).round_ties_even(), -2.0);

Only exact ties behave differently — 2.4999 and 2.5001 round the same either way:

1
2
assert_eq!(2.4999_f64.round_ties_even(), 2.0);
assert_eq!(2.5001_f64.round_ties_even(), 3.0);

This is the rounding mode IEEE 754 uses by default for a reason: it’s statistically unbiased over many operations. It’s also what Python’s built-in round does — if a value “rounds wrong” when porting Python code to Rust, this is why. Stable since Rust 1.77.

← Previous 302. cbrt — powf(1.0/3.0) Says the Cube Root of -27 Is NaN Next → 304. trunc & fract — Split a Float Without the Cast Round-Trip