Skip to main content

approx_eq_trait/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3
4// Based on the "Relative epsilon method" defined here:
5// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/.
6
7mod sealed {
8    pub trait Sealed {}
9
10    impl Sealed for f32 {}
11    impl Sealed for f64 {}
12}
13
14/// A trait for floating point equality.
15pub trait ApproxEq: sealed::Sealed {
16    fn approx_eq(self, other: Self) -> bool;
17}
18
19impl ApproxEq for f32 {
20    fn approx_eq(self, other: Self) -> bool {
21        let diff = (self - other).abs();
22        let a = self.abs();
23        let b = other.abs();
24        let largest = f32::max(a, b);
25        diff <= largest * f32::EPSILON
26    }
27}
28
29impl ApproxEq for f64 {
30    fn approx_eq(self, other: Self) -> bool {
31        let diff = (self - other).abs();
32        let a = self.abs();
33        let b = other.abs();
34        let largest = f64::max(a, b);
35        diff <= largest * f64::EPSILON
36    }
37}
38
39#[macro_export]
40macro_rules! assert_approx_eq {
41    ($a:expr, $b:expr$(,)?) => {{
42        let a = $a;
43        let b = $b;
44        assert!(
45            $crate::ApproxEq::approx_eq(a, b),
46            "assertion `{a}.approx_eq({b})` failed"
47        )
48    }};
49}
50
51#[macro_export]
52macro_rules! assert_approx_eq_slice {
53    ($a:expr, $b:expr$(,)?) => {{
54        let a = $a;
55        let b = $b;
56
57        let a: &[_] = ::core::convert::AsRef::as_ref(&a);
58        let b: &[_] = ::core::convert::AsRef::as_ref(&b);
59
60        assert_eq!(
61            a.len(),
62            b.len(),
63            "assertion `{a:?}.len() == {b:?}.len()` failed"
64        );
65
66        for (&a, &b) in a.iter().zip(b) {
67            $crate::assert_approx_eq!(a, b);
68        }
69    }};
70}