pub(super) fn count_to_f64(n: usize) -> f64 {
let wide = u64::try_from(n).unwrap_or(u64::MAX);
let hi = u32::try_from(wide >> 32).unwrap_or(0);
let lo = u32::try_from(wide & 0xFFFF_FFFF).unwrap_or(0);
f64::from(hi).mul_add(4_294_967_296.0, f64::from(lo))
}
pub(super) fn mean(values: &[f64]) -> f64 {
let n = count_to_f64(values.len());
if n == 0.0 {
return 0.0;
}
values.iter().sum::<f64>() / n
}
pub(super) fn population_variance(values: &[f64]) -> f64 {
let n = count_to_f64(values.len());
if n == 0.0 {
return 0.0;
}
let m = mean(values);
let ss: f64 = values
.iter()
.map(|&v| {
let d = v - m;
d * d
})
.sum();
ss / n
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn count_widens_exactly() {
assert!((count_to_f64(4096) - 4096.0).abs() < 1e-12, "widening");
}
#[test]
fn mean_matches_hand_value() {
let m = mean(&[2.0, 4.0, 6.0]);
assert!((m - 4.0).abs() < 1e-12, "mean was {m}");
}
#[test]
fn population_variance_uses_divisor_n() {
let v = population_variance(&[2.0, 4.0, 6.0]);
assert!(
(v - 2.666_666_666_666_666_5).abs() < 1e-12,
"variance was {v}"
);
}
}