ixa/
numeric.rs

1#![allow(clippy::approx_constant)]
2//! Vendored from [statrs@0.18.0 (prec.rs)](http://github.com/statrs-dev/statrs/blob/v0.18.0/src/prec.rs), convenience
3//! wrappers around methods from the approx crate. Provides utility functions for working with floating point precision.
4
5use approx::AbsDiffEq;
6
7/// Targeted accuracy instantiated over `f64`
8pub const ACC: f64 = 10e-11;
9
10/// Compares if two floats are close via `approx::abs_diff_eq` using a maximum absolute difference
11/// (epsilon) of `acc`.
12#[must_use]
13pub fn almost_eq(a: f64, b: f64, acc: f64) -> bool {
14    if a.is_infinite() && b.is_infinite() {
15        return a == b;
16    }
17    a.abs_diff_eq(&b, acc)
18}
19
20/// Compares if two floats are close via `approx::relative_eq!` and `ACC` relative precision.
21/// Updates first argument to value of second argument.
22#[must_use]
23pub fn convergence(x: &mut f64, x_new: f64) -> bool {
24    let res = approx::relative_eq!(*x, x_new, max_relative = ACC);
25    *x = x_new;
26    res
27}
28
29// Not from statrs.
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use crate::assert_almost_eq;
34
35    #[test]
36    fn almost_eq_within_tolerance() {
37        let a = 1.0;
38        let b = 1.0 + 0.5e-11;
39        // within ACC = 10e-11
40        assert!(almost_eq(a, b, ACC));
41    }
42
43    #[test]
44    fn almost_eq_outside_tolerance() {
45        let a = 1.0;
46        let b = 1.0 + 2e-10;
47        // 2e-10 > 10e-11
48        assert!(!almost_eq(a, b, ACC));
49    }
50
51    #[test]
52    fn almost_eq_infinities() {
53        assert!(almost_eq(f64::INFINITY, f64::INFINITY, ACC));
54        assert!(almost_eq(f64::NEG_INFINITY, f64::NEG_INFINITY, ACC));
55        assert!(!almost_eq(f64::INFINITY, f64::NEG_INFINITY, ACC));
56    }
57
58    #[test]
59    fn convergence_updates_and_compares() {
60        let mut x = 100.0;
61        // first call: compare 100.0 vs 100.0 → exactly equal → true
62        assert!(convergence(&mut x, 100.0));
63        // x should now be updated
64        assert_eq!(x, 100.0);
65
66        // now pick a new value within relative ACC
67        let x_new = x * (1.0 + 0.5 * ACC);
68        assert!(convergence(&mut x, x_new));
69        assert_eq!(x, x_new);
70
71        // now pick something well outside relative ACC
72        let x_new2 = x * (1.0 + 2.0 * ACC);
73        assert!(!convergence(&mut x, x_new2));
74        assert_eq!(x, x_new2);
75    }
76
77    #[test]
78    fn assert_almost_eq_macro_passes() {
79        // should not panic
80        assert_almost_eq!(3.14159265, 3.14159264, 1e-7);
81    }
82
83    #[test]
84    #[should_panic(expected = "assertion failed")]
85    fn assert_almost_eq_macro_panics() {
86        // difference is 1e-3, but prec=1e-4 → panic
87        assert_almost_eq!(1.0, 1.001, 1e-4);
88    }
89}