Skip to main content

docvortex_core/
exact_mean.rs

1//! 有限浮点数的精确二进制比率;范围过宽或累积溢出时完整使用 Python Fraction 路径。
2
3/// 将每个二进制浮点数拆成整数尾数和二次幂,保持精确总和而不做浮点累加。
4pub fn exact_sum_parts(values: &[f64]) -> Option<(i128, i32)> {
5    if values.is_empty() || values.iter().any(|value| !value.is_finite()) {
6        return None;
7    }
8    let mut parts = Vec::with_capacity(values.len());
9    let mut minimum = i32::MAX;
10    for value in values {
11        let bits = value.to_bits();
12        let exponent = ((bits >> 52) & 0x7ff) as i32;
13        let mut mantissa = bits & ((1_u64 << 52) - 1);
14        let mut power = if exponent == 0 {
15            -1074
16        } else {
17            mantissa |= 1_u64 << 52;
18            exponent - 1023 - 52
19        };
20        if mantissa == 0 {
21            continue;
22        }
23        let shift = mantissa.trailing_zeros();
24        mantissa >>= shift;
25        power += shift as i32;
26        minimum = minimum.min(power);
27        let mantissa = if bits >> 63 == 0 {
28            mantissa as i128
29        } else {
30            -(mantissa as i128)
31        };
32        parts.push((mantissa, power));
33    }
34    if parts.is_empty() {
35        return Some((0, 0));
36    }
37    let mut total = 0_i128;
38    for (mantissa, power) in parts {
39        let shift = (power - minimum) as u32;
40        if shift > 126 {
41            return None;
42        }
43        total = total.checked_add(mantissa.checked_mul(1_i128 << shift)?)?;
44    }
45    Some((total, minimum))
46}
47
48#[cfg(test)]
49mod tests {
50    /// 整数比率覆盖抵消、正负零、次正规数、巨大同阶数及不可安全累积时的完整回退。
51    #[test]
52    fn exact_parts_and_fallback() {
53        assert_eq!(super::exact_sum_parts(&[1.0, 2.0, 3.0]), Some((6, 0)));
54        assert_eq!(super::exact_sum_parts(&[1.0, -1.0]), Some((0, 0)));
55        assert_eq!(super::exact_sum_parts(&[-0.0, 0.0]), Some((0, 0)));
56        assert_eq!(
57            super::exact_sum_parts(&[f64::from_bits(1), f64::from_bits(1)]),
58            Some((2, -1074))
59        );
60        assert!(super::exact_sum_parts(&[1e308, 1e308]).is_some());
61        assert!(super::exact_sum_parts(&[1e308, 1e-308]).is_none());
62        assert!(super::exact_sum_parts(&[f64::NAN]).is_none());
63        assert!(super::exact_sum_parts(&[]).is_none());
64    }
65}