1use float_cmp::Ulps;
4
5pub trait ApproxEq {
6 fn approxeq(&self, other: &Self, _ulps: i64) -> bool;
7 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result;
8}
9
10impl ApproxEq for bool {
11 fn approxeq(&self, other: &Self, _ulps: i64) -> bool {
12 self == other
13 }
14
15 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
16 write!(f, "{:?}", self)
17 }
18}
19
20macro_rules! impl_integer_approxeq {
21 { $($type:ty),* } => {
22 $(
23 impl ApproxEq for $type {
24 fn approxeq(&self, other: &Self, _ulps: i64) -> bool {
25 self == other
26 }
27
28 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
29 write!(f, "{:?} ({:x})", self, self)
30 }
31 }
32 )*
33 };
34}
35
36impl_integer_approxeq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize }
37
38macro_rules! impl_float_approxeq {
39 { $($type:ty),* } => {
40 $(
41 impl ApproxEq for $type {
42 fn approxeq(&self, other: &Self, ulps: i64) -> bool {
43 if self.is_nan() && other.is_nan() {
44 true
45 } else {
46 (self.ulps(other) as i64).abs() <= ulps
47 }
48 }
49
50 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
51 write!(f, "{:?} ({:x})", self, self.to_bits())
52 }
53 }
54 )*
55 };
56}
57
58impl_float_approxeq! { f32, f64 }
59
60impl<T: ApproxEq, const N: usize> ApproxEq for [T; N] {
61 fn approxeq(&self, other: &Self, ulps: i64) -> bool {
62 self.iter()
63 .zip(other.iter())
64 .fold(true, |value, (left, right)| {
65 value && left.approxeq(right, ulps)
66 })
67 }
68
69 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
70 #[repr(transparent)]
71 struct Wrapper<'a, T: ApproxEq>(&'a T);
72
73 impl<T: ApproxEq> core::fmt::Debug for Wrapper<'_, T> {
74 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
75 self.0.fmt(f)
76 }
77 }
78
79 f.debug_list()
80 .entries(self.iter().map(|x| Wrapper(x)))
81 .finish()
82 }
83}
84
85#[doc(hidden)]
86pub struct ApproxEqWrapper<'a, T>(pub &'a T, pub i64);
87
88impl<T: ApproxEq> PartialEq<T> for ApproxEqWrapper<'_, T> {
89 fn eq(&self, other: &T) -> bool {
90 self.0.approxeq(other, self.1)
91 }
92}
93
94impl<T: ApproxEq> core::fmt::Debug for ApproxEqWrapper<'_, T> {
95 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
96 self.0.fmt(f)
97 }
98}
99
100#[macro_export]
101macro_rules! prop_assert_approxeq {
102 { $a:expr, $b:expr, $ulps:expr $(,)? } => {
103 {
104 use $crate::approxeq::ApproxEqWrapper;
105 let a = $a;
106 let b = $b;
107 proptest::prop_assert_eq!(ApproxEqWrapper(&a, $ulps), b);
108 }
109 };
110}