1
//! Utilities to compare floating-point numbers.
2

            
3
use float_cmp::ApproxEq;
4

            
5
// The following are copied from cairo/src/{cairo-fixed-private.h,
6
// cairo-fixed-type-private.h}
7

            
8
const CAIRO_FIXED_FRAC_BITS: u64 = 8;
9

            
10
const CAIRO_FIXED_MAX: i32 = i32::MAX;
11
const CAIRO_FIXED_MIN: i32 = i32::MIN;
12

            
13
/// The double that corresponds to (the number one in fixed-point representation)
14
const CAIRO_FIXED_ONE_DOUBLE: f64 = (1 << CAIRO_FIXED_FRAC_BITS) as f64;
15

            
16
/// The largest representable fixed-point number, as a double.  This is a bit over 8 million.
17
pub const CAIRO_FIXED_MAX_DOUBLE: f64 = (CAIRO_FIXED_MAX as f64) / CAIRO_FIXED_ONE_DOUBLE;
18

            
19
/// The most negative representable fixed-point number, as a double.  This is a bit less than -8 million.
20
pub const CAIRO_FIXED_MIN_DOUBLE: f64 = (CAIRO_FIXED_MIN as f64) / CAIRO_FIXED_ONE_DOUBLE;
21

            
22
/// Checks whether two floating-point numbers are approximately equal,
23
/// considering Cairo's limitations on numeric representation.
24
///
25
/// Cairo uses fixed-point numbers internally.  We implement this
26
/// trait for `f64`, so that two numbers can be considered "close
27
/// enough to equal" if their absolute difference is smaller than the
28
/// smallest fixed-point fraction that Cairo can represent.
29
///
30
/// Note that this trait is reliable even if the given numbers are
31
/// outside of the range that Cairo's fixed-point numbers can
32
/// represent.  In that case, we check for the absolute difference,
33
/// and finally allow a difference of 1 unit-in-the-last-place (ULP)
34
/// for very large f64 values.
35
pub trait ApproxEqCairo: ApproxEq {
36
    fn approx_eq_cairo(self, other: Self) -> bool;
37
}
38

            
39
impl ApproxEqCairo for f64 {
40
56233790
    fn approx_eq_cairo(self, other: f64) -> bool {
41
56233790
        let cairo_smallest_fraction = 1.0 / f64::from(1 << CAIRO_FIXED_FRAC_BITS);
42
56233790
        self.approx_eq(other, (cairo_smallest_fraction, 1))
43
56233790
    }
44
}
45

            
46
// Macro for usage in unit tests
47
#[doc(hidden)]
48
#[macro_export]
49
macro_rules! assert_approx_eq_cairo {
50
    ($left:expr, $right:expr) => {{
51
        match ($left, $right) {
52
            (l, r) => {
53
                if !l.approx_eq_cairo(r) {
54
                    panic!(
55
                        r#"assertion failed: `(left == right)`
56
  left: `{:?}`,
57
 right: `{:?}`"#,
58
                        l, r
59
                    )
60
                }
61
            }
62
        }
63
    }};
64
}
65

            
66
#[cfg(test)]
67
mod tests {
68
    use super::*;
69

            
70
    #[test]
71
1
    fn numbers_approx_equal() {
72
1
        // 0 == 1/256 - cairo can represent it, so not equal
73
1
        assert!(!0.0_f64.approx_eq_cairo(0.00390635_f64));
74

            
75
        // 1 == 1 + 1/256 - cairo can represent it, so not equal
76
1
        assert!(!1.0_f64.approx_eq_cairo(1.00390635_f64));
77

            
78
        // 0 == 1/256 - cairo can represent it, so not equal
79
1
        assert!(!0.0_f64.approx_eq_cairo(-0.00390635_f64));
80

            
81
        // 1 == 1 - 1/256 - cairo can represent it, so not equal
82
1
        assert!(!1.0_f64.approx_eq_cairo(0.99609365_f64));
83

            
84
        // 0 == 1/512 - cairo approximates to 0, so equal
85
1
        assert!(0.0_f64.approx_eq_cairo(0.001953125_f64));
86

            
87
        // 1 == 1 + 1/512 - cairo approximates to 1, so equal
88
1
        assert!(1.0_f64.approx_eq_cairo(1.001953125_f64));
89

            
90
        // 0 == -1/512 - cairo approximates to 0, so equal
91
1
        assert!(0.0_f64.approx_eq_cairo(-0.001953125_f64));
92

            
93
        // 1 == 1 - 1/512 - cairo approximates to 1, so equal
94
1
        assert!(1.0_f64.approx_eq_cairo(0.998046875_f64));
95

            
96
        // This is 2^53 compared to (2^53 + 2).  When represented as
97
        // f64, they are 1 unit-in-the-last-place (ULP) away from each
98
        // other, since the mantissa has 53 bits (52 bits plus 1
99
        // "hidden" bit).  The first number is an exact double, and
100
        // the second one is the next biggest double.  We consider a
101
        // difference of 1 ULP to mean that numbers are "equal", to
102
        // account for slight imprecision in floating-point
103
        // calculations.  Most of the time, for small values, we will
104
        // be using the cairo_smallest_fraction from the
105
        // implementation of approx_eq_cairo() above.  For large
106
        // values, we want the ULPs.
107
        //
108
        // In the second assertion, we compare 2^53 with (2^53 + 4).  Those are
109
        // 2 ULPs away, and we don't consider them equal.
110
1
        assert!(9_007_199_254_740_992.0.approx_eq_cairo(9_007_199_254_740_994.0));
111
1
        assert!(!9_007_199_254_740_992.0.approx_eq_cairo(9_007_199_254_740_996.0));
112
1
    }
113

            
114
    #[test]
115
1
    fn assert_approx_eq_cairo_should_not_panic() {
116
1
        assert_approx_eq_cairo!(42_f64, 42_f64);
117
1
    }
118

            
119
    #[test]
120
    #[should_panic]
121
1
    fn assert_approx_eq_cairo_should_panic() {
122
1
        assert_approx_eq_cairo!(3_f64, 42_f64);
123
    }
124
}