Files

48 lines
1.7 KiB
Rust

//! `ExactSum::mean`: the exact total over a count, rounded once, against exact rationals.
//! Evidence: `context/test-evidence/numtower.md`.
mod oracle;
use num_bigint::BigInt;
use num_rational::BigRational;
use numtower::ExactSum;
use oracle::{finite_f64, is_nearest};
use proptest::prelude::*;
fn sum_of(values: &[f64]) -> ExactSum {
let mut sum = ExactSum::new();
values.iter().for_each(|v| sum.add(*v, 1));
sum
}
#[test]
fn a_mean_whose_sum_would_overflow_is_still_finite() {
let sum = sum_of(&[f64::MAX, f64::MAX]);
assert_eq!(sum.mean(2), f64::MAX, "the sum is inf, the mean is not");
}
#[test]
fn non_finite_sums_have_non_finite_means_and_zero_keeps_its_sign() {
assert_eq!(sum_of(&[f64::INFINITY, 1.0]).mean(2), f64::INFINITY);
assert_eq!(sum_of(&[f64::NEG_INFINITY]).mean(3), f64::NEG_INFINITY);
assert!(sum_of(&[f64::NAN, 1.0]).mean(2).is_nan());
assert!(sum_of(&[f64::INFINITY, f64::NEG_INFINITY]).mean(2).is_nan());
assert_eq!(sum_of(&[-0.0, -0.0]).mean(2).to_bits(), (-0.0f64).to_bits());
assert_eq!(sum_of(&[1.0, -1.0]).mean(2).to_bits(), 0.0f64.to_bits());
}
proptest! {
#[test]
fn the_mean_is_the_nearest_f64_to_the_exact_total_over_the_count(
values in prop::collection::vec(finite_f64(), 1..9),
) {
let total = values
.iter()
.map(|v| BigRational::from_float(*v).unwrap())
.fold(BigRational::from_integer(BigInt::from(0)), |a, b| a + b);
let exact = total / BigRational::from_integer(BigInt::from(values.len()));
let got = sum_of(&values).mean(values.len() as u64);
prop_assert!(is_nearest(got, &exact), "{got} for {values:?}");
}
}