approx/
ulps_eq.rs

1use core::cell;
2#[cfg(feature = "num-complex")]
3use num_complex::Complex;
4use num_traits::Signed;
5
6use AbsDiffEq;
7
8/// Equality comparisons between two numbers using both the absolute difference and ULPs
9/// (Units in Last Place) based comparisons.
10pub trait UlpsEq<Rhs = Self>: AbsDiffEq<Rhs>
11where
12    Rhs: ?Sized,
13{
14    /// The default ULPs to tolerate when testing values that are far-apart.
15    ///
16    /// This is used when no `max_ulps` value is supplied to the [`ulps_eq`] macro.
17    fn default_max_ulps() -> u32;
18
19    /// A test for equality that uses units in the last place (ULP) if the values are far apart.
20    fn ulps_eq(&self, other: &Rhs, epsilon: Self::Epsilon, max_ulps: u32) -> bool;
21
22    /// The inverse of [`UlpsEq::ulps_eq`].
23    fn ulps_ne(&self, other: &Rhs, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
24        !Self::ulps_eq(self, other, epsilon, max_ulps)
25    }
26}
27
28///////////////////////////////////////////////////////////////////////////////////////////////////
29// Base implementations
30///////////////////////////////////////////////////////////////////////////////////////////////////
31
32// Implementation based on: [Comparing Floating Point Numbers, 2012 Edition]
33// (https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
34macro_rules! impl_ulps_eq {
35    ($T:ident, $U:ident) => {
36        impl UlpsEq for $T {
37            #[inline]
38            fn default_max_ulps() -> u32 {
39                4
40            }
41
42            #[inline]
43            fn ulps_eq(&self, other: &$T, epsilon: $T, max_ulps: u32) -> bool {
44                // For when the numbers are really close together
45                if $T::abs_diff_eq(self, other, epsilon) {
46                    return true;
47                }
48
49                // Trivial negative sign check
50                if self.signum() != other.signum() {
51                    return false;
52                }
53
54                // ULPS difference comparison
55                let int_self: $U = self.to_bits();
56                let int_other: $U = other.to_bits();
57
58                // To be replaced with `abs_sub`, if
59                // https://github.com/rust-lang/rust/issues/62111 lands.
60                if int_self <= int_other {
61                    int_other - int_self <= max_ulps as $U
62                } else {
63                    int_self - int_other <= max_ulps as $U
64                }
65            }
66        }
67    };
68}
69
70impl_ulps_eq!(f32, u32);
71impl_ulps_eq!(f64, u64);
72
73///////////////////////////////////////////////////////////////////////////////////////////////////
74// Derived implementations
75///////////////////////////////////////////////////////////////////////////////////////////////////
76
77impl<'a, T: UlpsEq + ?Sized> UlpsEq for &'a T {
78    #[inline]
79    fn default_max_ulps() -> u32 {
80        T::default_max_ulps()
81    }
82
83    #[inline]
84    fn ulps_eq(&self, other: &&'a T, epsilon: T::Epsilon, max_ulps: u32) -> bool {
85        T::ulps_eq(*self, *other, epsilon, max_ulps)
86    }
87}
88
89impl<'a, T: UlpsEq + ?Sized> UlpsEq for &'a mut T {
90    #[inline]
91    fn default_max_ulps() -> u32 {
92        T::default_max_ulps()
93    }
94
95    #[inline]
96    fn ulps_eq(&self, other: &&'a mut T, epsilon: T::Epsilon, max_ulps: u32) -> bool {
97        T::ulps_eq(*self, *other, epsilon, max_ulps)
98    }
99}
100
101impl<T: UlpsEq + Copy> UlpsEq for cell::Cell<T> {
102    #[inline]
103    fn default_max_ulps() -> u32 {
104        T::default_max_ulps()
105    }
106
107    #[inline]
108    fn ulps_eq(&self, other: &cell::Cell<T>, epsilon: T::Epsilon, max_ulps: u32) -> bool {
109        T::ulps_eq(&self.get(), &other.get(), epsilon, max_ulps)
110    }
111}
112
113impl<T: UlpsEq + ?Sized> UlpsEq for cell::RefCell<T> {
114    #[inline]
115    fn default_max_ulps() -> u32 {
116        T::default_max_ulps()
117    }
118
119    #[inline]
120    fn ulps_eq(&self, other: &cell::RefCell<T>, epsilon: T::Epsilon, max_ulps: u32) -> bool {
121        T::ulps_eq(&self.borrow(), &other.borrow(), epsilon, max_ulps)
122    }
123}
124
125impl<A, B> UlpsEq<[B]> for [A]
126where
127    A: UlpsEq<B>,
128    A::Epsilon: Clone,
129{
130    #[inline]
131    fn default_max_ulps() -> u32 {
132        A::default_max_ulps()
133    }
134
135    #[inline]
136    fn ulps_eq(&self, other: &[B], epsilon: A::Epsilon, max_ulps: u32) -> bool {
137        self.len() == other.len()
138            && Iterator::zip(self.iter(), other)
139                .all(|(x, y)| A::ulps_eq(x, y, epsilon.clone(), max_ulps))
140    }
141}
142
143#[cfg(feature = "num-complex")]
144impl<T: UlpsEq> UlpsEq for Complex<T>
145where
146    T::Epsilon: Clone,
147{
148    #[inline]
149    fn default_max_ulps() -> u32 {
150        T::default_max_ulps()
151    }
152
153    #[inline]
154    fn ulps_eq(&self, other: &Complex<T>, epsilon: T::Epsilon, max_ulps: u32) -> bool {
155        T::ulps_eq(&self.re, &other.re, epsilon.clone(), max_ulps)
156            && T::ulps_eq(&self.im, &other.im, epsilon, max_ulps)
157    }
158}