fix(core): the exactness test merges both ways and from_parts refuses sums that contradict their counts (improvise-qddo)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
committed by
Edward Langley
parent
8ba4bd0a6c
commit
f7404778b0
@@ -60,7 +60,13 @@ impl Numbers {
|
||||
greatest: Option<f64>,
|
||||
) -> Result<Self, BadParts> {
|
||||
let bad = |why| Err(BadParts(why));
|
||||
if count == 0 && sum != ExactSum::new() {
|
||||
return bad("a sum with no numbers");
|
||||
}
|
||||
match (least, greatest) {
|
||||
(None, None) if count > 0 && !sum.value().is_nan() => {
|
||||
return bad("no extremes but a sum that is not NaN");
|
||||
}
|
||||
(None, None) => {}
|
||||
(Some(_), Some(_)) if count == 0 => return bad("extremes with no numbers"),
|
||||
(Some(l), Some(g)) if l.is_nan() || g.is_nan() => return bad("a NaN extreme"),
|
||||
@@ -462,13 +468,15 @@ mod tests {
|
||||
#[test]
|
||||
fn the_exact_sum_of_a_pool_of_cancelling_values_is_not_the_sum_of_rounded_parts() {
|
||||
let rounded = [1e16, 1.0].iter().sum::<f64>() + [-1e16].iter().sum::<f64>();
|
||||
let mut pooled = Numbers::of([1e16, 1.0]);
|
||||
pooled.merge(&Numbers::of([-1e16]));
|
||||
assert_eq!(
|
||||
rounded, 0.0,
|
||||
"the fixture is inexact: summing rounded parts loses the 1"
|
||||
"the fixture is inexact: rounded parts lose the 1"
|
||||
);
|
||||
assert_eq!(pooled.total(), 1.0);
|
||||
for (first, second) in [([1e16, 1.0], [-1e16, 0.0]), ([-1e16, 0.0], [1e16, 1.0])] {
|
||||
let mut pooled = Numbers::of(first);
|
||||
pooled.merge(&Numbers::of(second));
|
||||
assert_eq!(pooled.total(), 1.0, "{first:?} then {second:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn pair(c: &str, i: &str) -> (String, String) {
|
||||
@@ -701,7 +709,19 @@ mod tests {
|
||||
refused(Numbers::from_parts(2, sum(), Some(1.0), None)),
|
||||
"one extreme without the other"
|
||||
);
|
||||
let ok = Numbers::from_parts(2, sum(), None, None).unwrap();
|
||||
let mut one = ExactSum::new();
|
||||
one.add(1.0, 1);
|
||||
assert_eq!(
|
||||
refused(Numbers::from_parts(0, one.clone(), None, None)),
|
||||
"a sum with no numbers"
|
||||
);
|
||||
assert_eq!(
|
||||
refused(Numbers::from_parts(2, one, None, None)),
|
||||
"no extremes but a sum that is not NaN"
|
||||
);
|
||||
let mut nan = ExactSum::new();
|
||||
nan.add(f64::NAN, 1);
|
||||
let ok = Numbers::from_parts(2, nan, None, None).unwrap();
|
||||
assert!(
|
||||
ok.reduce(&AggFunc::Min).unwrap().is_nan(),
|
||||
"all NaN: no extremes, a count"
|
||||
|
||||
Reference in New Issue
Block a user