1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#![allow(clippy::float_cmp)]

use crate::{FloatDiff, FloatEq, FloatEqAll, FloatEqAllDebug, FloatEqDebug};

macro_rules! impl_traits {
    ($float:ident, $uint:ident) => {
        mod $float {
            #[cfg(feature = "std")]
            #[inline]
            pub(crate) fn abs(value: $float) -> $float {
                // use the intrinsic for std builds
                value.abs()
            }

            #[cfg(not(feature = "std"))]
            #[inline]
            pub(crate) fn abs(value: $float) -> $float {
                // mask away only the sign bit for no_std builds since the abs
                // method is not available
                const MASK: $uint = !(1 << ((::core::mem::size_of::<$float>() * 8) - 1));
                $float::from_bits(value.to_bits() & MASK)
            }
        }

        impl FloatDiff for $float {
            type AbsDiff = Self;
            type UlpsDiff = $uint;

            #[inline]
            fn abs_diff(&self, other: &Self) -> Self {
                $float::abs(self - other)
            }

            #[inline]
            fn ulps_diff(&self, other: &Self) -> Option<Self::UlpsDiff> {
                if self == other {
                    Some(0)
                } else if self.is_nan() || other.is_nan() {
                    None
                } else if self.is_sign_positive() != other.is_sign_positive() {
                    None
                } else {
                    let a = self.to_bits();
                    let b = other.to_bits();
                    let max = a.max(b);
                    let min = a.min(b);
                    Some(max - min)
                }
            }
        }

        impl FloatEq for $float {
            type Epsilon = $float;
            type UlpsEpsilon = $uint;

            #[inline]
            fn eq_abs(&self, other: &Self, max_diff: &Self::Epsilon) -> bool {
                // the PartialEq check covers equality of infinities
                self == other || $float::abs(self - other).le(max_diff)
            }

            #[inline]
            fn eq_rel(&self, other: &Self, max_diff: &Self::Epsilon) -> bool {
                // the PartialEq check covers equality of infinities
                self == other || {
                    let largest = $float::abs(*self).max($float::abs(*other));
                    let epsilon = largest * max_diff;
                    self.abs_diff(other) <= epsilon
                }
            }

            #[inline]
            fn eq_ulps(&self, other: &Self, max_diff: &Self::UlpsEpsilon) -> bool {
                if self.is_nan() || other.is_nan() {
                    false // NaNs are never equal
                } else if self.is_sign_positive() != other.is_sign_positive() {
                    self == other // account for zero == negative zero
                } else {
                    let a = self.to_bits();
                    let b = other.to_bits();
                    let max = a.max(b);
                    let min = a.min(b);
                    (max - min).le(max_diff)
                }
            }
        }

        impl FloatEqAll for $float {
            type Epsilon = <$float as FloatEq>::Epsilon;
            type UlpsEpsilon = <$float as FloatEq>::UlpsEpsilon;

            #[inline]
            fn eq_abs_all(&self, other: &Self, max_diff: &Self::Epsilon) -> bool {
                self.eq_abs(other, max_diff)
            }

            #[inline]
            fn eq_rel_all(&self, other: &Self, max_diff: &Self::Epsilon) -> bool {
                self.eq_rel(other, max_diff)
            }

            #[inline]
            fn eq_ulps_all(&self, other: &Self, max_diff: &Self::UlpsEpsilon) -> bool {
                self.eq_ulps(other, max_diff)
            }
        }

        impl FloatEqDebug for $float {
            type DebugEpsilon = <Self as FloatEq>::Epsilon;
            type DebugUlpsEpsilon = <Self as FloatEq>::UlpsEpsilon;

            #[inline]
            fn debug_abs_epsilon(
                &self,
                _other: &Self,
                max_diff: &<Self as FloatEq>::Epsilon,
            ) -> Self::DebugEpsilon {
                *max_diff
            }

            #[inline]
            fn debug_rel_epsilon(
                &self,
                other: &Self,
                max_diff: &<Self as FloatEq>::Epsilon,
            ) -> Self::DebugEpsilon {
                $float::abs(*self).max($float::abs(*other)) * max_diff
            }

            #[inline]
            fn debug_ulps_epsilon(
                &self,
                _other: &Self,
                max_diff: &<Self as FloatEq>::UlpsEpsilon,
            ) -> Self::DebugUlpsEpsilon {
                *max_diff
            }
        }

        impl FloatEqAllDebug for $float {
            type DebugEpsilon = <Self as FloatEqAll>::Epsilon;
            type DebugUlpsEpsilon = <Self as FloatEqAll>::UlpsEpsilon;

            #[inline]
            fn debug_abs_all_epsilon(
                &self,
                other: &Self,
                max_diff: &<Self as FloatEqAll>::Epsilon,
            ) -> Self::DebugEpsilon {
                self.debug_abs_epsilon(other, max_diff)
            }

            #[inline]
            fn debug_rel_all_epsilon(
                &self,
                other: &Self,
                max_diff: &<Self as FloatEq>::Epsilon,
            ) -> Self::DebugEpsilon {
                self.debug_rel_epsilon(other, max_diff)
            }

            #[inline]
            fn debug_ulps_all_epsilon(
                &self,
                other: &Self,
                max_diff: &<Self as FloatEq>::UlpsEpsilon,
            ) -> Self::DebugUlpsEpsilon {
                self.debug_ulps_epsilon(other, max_diff)
            }
        }
    };
}

impl_traits!(f32, u32);
impl_traits!(f64, u64);