assertables/assert_approx/
assert_approx_eq.rs

1//! Assert a number is approximately equal to another.
2//!
3//! Pseudocode:<br>
4//! | a - b | ≤ 1e-6
5//!
6//! # Example
7//!
8//! ```rust
9//! use assertables::*;
10//!
11//! let a: f32 = 1.0000001;
12//! let b: f32 = 1.0000011;
13//! assert_approx_eq!(a, b);
14//! ```
15//!
16//!
17//! ## Comparisons
18//!
19//! This crate provides macro groups that test approximations and nearness:
20//!
21//! * [`assert_approx_eq`](macro@crate::assert_approx_eq) and
22//!   [`assert_approx_ne`](macro@crate::assert_approx_ne) test the approximate
23//!   equality within 1e-6. The macro name and the approximate value are chosen
24//!   to be similar to the longtime popular rust crate `assert_approx_eq`.
25//!
26//! * [`assert_in_delta`](macro@crate::assert_in_delta) tests the absolute error
27//!   (i.e. delta). This is the magnitude of the difference between the exact
28//!   value and the approximation.
29//!
30//! * [`assert_in_epsilon`](macro@crate::assert_in_epsilon) tests the relative
31//!   error (i.e. epsilon). This is the absolute error divided by the magnitude
32//!   of the exact value. This can be used to compare approximations of numbers
33//!   of wildly differing size.
34//!
35//! Examples:
36//!
37//! * Approximating the number 100 and 103 has an absolute error (approx) of 3
38//!   and a relative error (epsilon) of 0.03.
39//!
40//! * Approximating the number 1,000,000 and 1,000,003 has an absolute error
41//!   (approx) of 3, and a relative error (epsilon) of 0.000003.
42//!
43//! * For many kinds of applications, the relative error is more important than
44//!   the absolute error.
45//!
46//!
47//! ## Thanks
48//!
49//! * Thanks to [Ashley Williams](https://github.com/ashleygwilliams) for
50//!   creating and maintaining the `assert_approx_eq` crate.
51//!
52//! * Thanks to [Ryan Davis](https://github.com/zenspider) and Ruby minitest for
53//!   creating and maintaining `assert_in_approx` and `assert_in_epsilon` code.
54//!
55//!
56//! # Module macros
57//!
58//! * [`assert_approx_eq`](macro@crate::assert_approx_eq)
59//! * [`assert_approx_eq_as_result`](macro@crate::assert_approx_eq_as_result)
60//! * [`debug_assert_approx_eq`](macro@crate::debug_assert_approx_eq)
61
62/// Assert a number is approximately equal to another.
63///
64/// Pseudocode:<br>
65/// | a - b | ≤ 1e-6
66///
67/// * If true, return Result `Ok(abs_diff, approx)`.
68///
69/// * When false, return [`Err`] with a message and the values of the
70///   expressions with their debug representations.
71///
72/// This macro provides the same statements as [`assert_`](macro.assert_.html), except this macro
73/// returns a Result, rather than doing a panic.
74///
75/// This macro is useful for runtime checks, such as checking parameters, or
76/// sanitizing inputs, or handling different results in different ways.
77///
78/// # Module macros
79///
80/// * [`assert_approx_eq`](macro@crate::assert_approx_eq)
81/// * [`assert_approx_eq_as_result`](macro@crate::assert_approx_eq_as_result)
82/// * [`debug_assert_approx_eq`](macro@crate::debug_assert_approx_eq)
83///
84#[macro_export]
85macro_rules! assert_approx_eq_as_result {
86    ($a:expr, $b:expr $(,)?) => {
87        match (&$a, &$b) {
88            (a, b) => {
89                let abs_diff = if (a >= b) { a - b } else { b - a };
90                let approx = 1.0e-6;
91                if abs_diff <= approx {
92                    Ok((abs_diff, approx))
93                } else {
94                    Err(format!(
95                        concat!(
96                            "assertion failed: `assert_approx_eq!(a, b)`\n",
97                            "https://docs.rs/assertables/",
98                            env!("CARGO_PKG_VERSION"),
99                            "/assertables/macro.assert_approx_eq.html\n",
100                            "            a label: `{}`,\n",
101                            "            a debug: `{:?}`,\n",
102                            "            b label: `{}`,\n",
103                            "            b debug: `{:?}`,\n",
104                            "          | a - b |: `{:?}`,\n",
105                            "             approx: `{:?}`,\n",
106                            " | a - b | ≤ approx: false"
107                        ),
108                        stringify!($a),
109                        a,
110                        stringify!($b),
111                        b,
112                        abs_diff,
113                        approx
114                    ))
115                }
116            }
117        }
118    };
119}
120
121#[cfg(test)]
122mod test_assert_approx_eq_as_result {
123    use std::sync::Once;
124
125    #[test]
126    fn eq() {
127        let a: f32 = 1.0000001;
128        let b: f32 = 1.0000011;
129        for _ in 0..1 {
130            let actual = assert_approx_eq_as_result!(a, b);
131            assert_eq!(actual.unwrap(), (9.536743e-7, 1e-6));
132        }
133    }
134
135    #[test]
136    fn eq_once() {
137        static A: Once = Once::new();
138        fn a() -> f32 {
139            if A.is_completed() {
140                panic!("A.is_completed()")
141            } else {
142                A.call_once(|| {})
143            }
144            1.0000001
145        }
146
147        static B: Once = Once::new();
148        fn b() -> f32 {
149            if B.is_completed() {
150                panic!("B.is_completed()")
151            } else {
152                B.call_once(|| {})
153            }
154            1.0000011
155        }
156
157        assert_eq!(A.is_completed(), false);
158        assert_eq!(B.is_completed(), false);
159        let result = assert_approx_eq_as_result!(a(), b());
160        assert!(result.is_ok());
161        assert_eq!(A.is_completed(), true);
162        assert_eq!(B.is_completed(), true);
163    }
164
165    #[test]
166    fn ne() {
167        let a: f32 = 1.0000001;
168        let b: f32 = 1.0000012;
169        let actual = assert_approx_eq_as_result!(a, b);
170        let message = concat!(
171            "assertion failed: `assert_approx_eq!(a, b)`\n",
172            "https://docs.rs/assertables/",
173            env!("CARGO_PKG_VERSION"),
174            "/assertables/macro.assert_approx_eq.html\n",
175            "            a label: `a`,\n",
176            "            a debug: `1.0000001`,\n",
177            "            b label: `b`,\n",
178            "            b debug: `1.0000012`,\n",
179            "          | a - b |: `1.0728836e-6`,\n",
180            "             approx: `1e-6`,\n",
181            " | a - b | ≤ approx: false"
182        );
183        assert_eq!(actual.unwrap_err(), message);
184    }
185}
186
187/// Assert a number is approximately equal to another.
188///
189/// Pseudocode:<br>
190/// | a - b | ≤ 1e-6
191///
192/// * If true, return `(diff, approx)`.
193///
194/// * Otherwise, call [`panic!`] with a message and the values of the
195///   expressions with their debug representations.
196///
197/// # Examples
198///
199/// ```rust
200/// use assertables::*;
201/// # use std::panic;
202///
203/// # fn main() {
204/// let a: f32 = 1.0000001;
205/// let b: f32 = 1.0000011;
206/// assert_approx_eq!(a, b);
207///
208/// # let result = panic::catch_unwind(|| {
209/// // This will panic
210/// let a: f32 = 1.0000001;
211/// let b: f32 = 1.0000012;
212/// assert_approx_eq!(a, b);
213/// # });
214/// // assertion failed: `assert_approx_eq!(a, b)`
215/// // https://docs.rs/assertables/9.7.0/assertables/macro.assert_approx_eq.html
216/// //             a label: `a`,
217/// //             a debug: `1.0000001`,
218/// //             b label: `b`,
219/// //             b debug: `1.0000012`,
220/// //           | a - b |: `1.0728836e-6`,
221/// //              approx: `1e-6`,
222/// //  | a - b | ≤ approx: false
223/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
224/// # let message = concat!(
225/// #     "assertion failed: `assert_approx_eq!(a, b)`\n",
226/// #     "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_approx_eq.html\n",
227/// #     "            a label: `a`,\n",
228/// #     "            a debug: `1.0000001`,\n",
229/// #     "            b label: `b`,\n",
230/// #     "            b debug: `1.0000012`,\n",
231/// #     "          | a - b |: `1.0728836e-6`,\n",
232/// #     "             approx: `1e-6`,\n",
233/// #     " | a - b | ≤ approx: false",
234/// # );
235/// # assert_eq!(actual, message);
236/// # }
237/// ```
238///
239/// The macros `assert_approx_eq` and `assert_in_epsilon` can test
240/// approximations:
241///
242/// * For an approximation, the absolute error (i.e. approx) is the magnitude of
243///   the difference between the exact value and the approximation. For this,
244///  use the macro
245///
246/// * For an approximation, the relative error (i.e. epsilon) is the absolute
247///   error divided by the magnitude of the exact value. This can be used to
248///   compare approximations of numbers of wildly differing size.
249///
250/// * For example, approximating the number 1,000 with an absolute error of 3
251///   is, in most applications, much worse than approximating the number
252///   1,000,000 with an absolute error of 3; in the first case the relative
253///   error is 0.003 and in the second it is only 0.000003.
254///
255/// * Thanks to Ruby minitest for the example and documentation.
256///
257/// # Module macros
258///
259/// * [`assert_approx_eq`](macro@crate::assert_approx_eq)
260/// * [`assert_approx_eq_as_result`](macro@crate::assert_approx_eq_as_result)
261/// * [`debug_assert_approx_eq`](macro@crate::debug_assert_approx_eq)
262///
263#[macro_export]
264macro_rules! assert_approx_eq {
265    ($a:expr, $b:expr $(,)?) => {
266        match $crate::assert_approx_eq_as_result!($a, $b) {
267            Ok(x) => x,
268            Err(err) => panic!("{}", err),
269        }
270    };
271    ($a:expr, $b:expr, $($message:tt)+) => {
272        match $crate::assert_approx_eq_as_result!($a, $b) {
273            Ok(x) => x,
274            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
275        }
276    };
277}
278
279#[cfg(test)]
280mod test_assert_approx_eq {
281    use std::panic;
282
283    #[test]
284    fn eq() {
285        let a: f32 = 1.0000001;
286        let b: f32 = 1.0000011;
287        for _ in 0..1 {
288            let actual = assert_approx_eq!(a, b);
289            assert_eq!(actual, (9.536743e-7, 1e-6));
290        }
291    }
292
293    #[test]
294    fn ne() {
295        let result = panic::catch_unwind(|| {
296            let a: f32 = 1.0000001;
297            let b: f32 = 1.0000012;
298            let _actual = assert_approx_eq!(a, b);
299        });
300        let message = concat!(
301            "assertion failed: `assert_approx_eq!(a, b)`\n",
302            "https://docs.rs/assertables/",
303            env!("CARGO_PKG_VERSION"),
304            "/assertables/macro.assert_approx_eq.html\n",
305            "            a label: `a`,\n",
306            "            a debug: `1.0000001`,\n",
307            "            b label: `b`,\n",
308            "            b debug: `1.0000012`,\n",
309            "          | a - b |: `1.0728836e-6`,\n",
310            "             approx: `1e-6`,\n",
311            " | a - b | ≤ approx: false"
312        );
313        assert_eq!(
314            result
315                .unwrap_err()
316                .downcast::<String>()
317                .unwrap()
318                .to_string(),
319            message
320        );
321    }
322}
323
324/// Assert a number is approximately equal to another.
325///
326/// Pseudocode:<br>
327/// | a - b | ≤ 1e-6
328///
329/// This macro provides the same statements as [`assert_approx_eq`](macro.assert_approx_eq.html),
330/// except this macro's statements are only enabled in non-optimized
331/// builds by default. An optimized build will not execute this macro's
332/// statements unless `-C debug-assertions` is passed to the compiler.
333///
334/// This macro is useful for checks that are too expensive to be present
335/// in a release build but may be helpful during development.
336///
337/// The result of expanding this macro is always type checked.
338///
339/// An unchecked assertion allows a program in an inconsistent state to
340/// keep running, which might have unexpected consequences but does not
341/// introduce unsafety as long as this only happens in safe code. The
342/// performance cost of assertions, however, is not measurable in general.
343/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
344/// after thorough profiling, and more importantly, only in safe code!
345///
346/// This macro is intended to work in a similar way to
347/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
348///
349/// # Module macros
350///
351/// * [`assert_approx_eq`](macro@crate::assert_approx_eq)
352/// * [`assert_approx_eq`](macro@crate::assert_approx_eq)
353/// * [`debug_assert_approx_eq`](macro@crate::debug_assert_approx_eq)
354///
355#[macro_export]
356macro_rules! debug_assert_approx_eq {
357    ($($arg:tt)*) => {
358        if $crate::cfg!(debug_assertions) {
359            $crate::assert_approx_eq!($($arg)*);
360        }
361    };
362}