diff --git a/approx-derive/src/rel_diff_eq.rs b/approx-derive/src/rel_diff_eq.rs index bfd80bf..2952144 100644 --- a/approx-derive/src/rel_diff_eq.rs +++ b/approx-derive/src/rel_diff_eq.rs @@ -232,6 +232,7 @@ impl AbsDiffEqParser { pub fn implement_derive_rel_diff_eq(&self) -> proc_macro2::TokenStream { let obj_name = &self.base_type.ident(); + let epsilon_type = self.get_epsilon_parent_type(); let max_relative_default_value = self.get_max_relative_default_value(); let (impl_generics, ty_generics, _) = self.base_type.generics().split_for_impl(); @@ -251,6 +252,10 @@ impl AbsDiffEqParser { impl #impl_generics #ApproxName::RelativeEq for #obj_name #ty_generics #where_clause { + fn default_relative_epsilon() -> Self::Epsilon { + <#epsilon_type as #ApproxName::RelativeEq>::default_relative_epsilon() + } + fn default_max_relative() -> Self::Epsilon { #max_relative_default_value } @@ -280,6 +285,10 @@ impl AbsDiffEqParser { impl #impl_generics #ApproxName::RelativeEq for #obj_name #ty_generics #where_clause { + fn default_relative_epsilon() -> Self::Epsilon { + <#epsilon_type as #ApproxName::RelativeEq>::default_relative_epsilon() + } + fn default_max_relative() -> Self::Epsilon { #max_relative_default_value } diff --git a/approx/src/abs_diff_eq.rs b/approx/src/abs_diff_eq.rs index d69ae60..eeb4596 100644 --- a/approx/src/abs_diff_eq.rs +++ b/approx/src/abs_diff_eq.rs @@ -39,8 +39,7 @@ where /// The default tolerance to use when testing values that are close together. /// /// This is used when no `epsilon` value is supplied to the - /// [`abs_diff_eq!`](crate::abs_diff_eq), [`relative_eq!`](crate::relative_eq), - /// or [`ulps_eq!`](crate::ulps_eq) macros. + /// [`abs_diff_eq!`](crate::abs_diff_eq), or [`ulps_eq!`](crate::ulps_eq) macros. fn default_epsilon() -> Self::Epsilon; /// A test for equality that uses the absolute difference to compute the approximate diff --git a/approx/src/lib.rs b/approx/src/lib.rs index a999031..51b7e5d 100644 --- a/approx/src/lib.rs +++ b/approx/src/lib.rs @@ -113,6 +113,7 @@ //! # } //! # } //! # impl RelativeEq for Complex where T::Epsilon: Copy { +//! # fn default_relative_epsilon() -> T::Epsilon { T::default_relative_epsilon() } //! # fn default_max_relative() -> T::Epsilon { T::default_max_relative() } //! # fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) //! # -> bool { @@ -163,6 +164,10 @@ //! impl RelativeEq for Complex where //! T::Epsilon: Copy, //! { +//! fn default_relative_epsilon() -> T::Epsilon { +//! T::default_relative_epsilon() +//! } +//! //! fn default_max_relative() -> T::Epsilon { //! T::default_max_relative() //! } @@ -339,7 +344,7 @@ where #[inline] fn default() -> Relative { Relative { - epsilon: A::default_epsilon(), + epsilon: A::default_relative_epsilon(), max_relative: A::default_max_relative(), } } diff --git a/approx/src/relative_eq.rs b/approx/src/relative_eq.rs index 685bc51..1356edb 100644 --- a/approx/src/relative_eq.rs +++ b/approx/src/relative_eq.rs @@ -37,6 +37,12 @@ pub trait RelativeEq: AbsDiffEq where Rhs: ?Sized, { + /// The default epsilon for testing values that are close to zero + /// + /// This is used when no `epsilon` value is supplied to the + /// [`relative_eq`](crate::relative_eq) macro. + fn default_relative_epsilon() -> Self::Epsilon; + /// The default relative tolerance for testing values that are far-apart. /// /// This is used when no `max_relative` value is supplied to the @@ -67,6 +73,11 @@ where macro_rules! impl_relative_eq { ($T:ident) => { impl RelativeEq for $T { + #[inline] + fn default_relative_epsilon() -> $T { + $T::MIN_POSITIVE + } + #[inline] fn default_max_relative() -> $T { $T::EPSILON @@ -117,6 +128,11 @@ impl_relative_eq!(f64); /////////////////////////////////////////////////////////////////////////////////////////////////// impl RelativeEq for Option { + #[inline] + fn default_relative_epsilon() -> T::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> T::Epsilon { T::default_max_relative() @@ -138,6 +154,11 @@ impl RelativeEq for Option { } impl RelativeEq for Result { + #[inline] + fn default_relative_epsilon() -> (T::Epsilon, E::Epsilon) { + (T::default_relative_epsilon(), E::default_relative_epsilon()) + } + #[inline] fn default_max_relative() -> (T::Epsilon, E::Epsilon) { (T::default_max_relative(), E::default_max_relative()) @@ -159,6 +180,11 @@ impl RelativeEq for Result { } impl<'a, T: RelativeEq + ?Sized> RelativeEq for &'a T { + #[inline] + fn default_relative_epsilon() -> T::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> T::Epsilon { T::default_max_relative() @@ -171,6 +197,11 @@ impl<'a, T: RelativeEq + ?Sized> RelativeEq for &'a T { } impl<'a, T: RelativeEq + ?Sized> RelativeEq for &'a mut T { + #[inline] + fn default_relative_epsilon() -> T::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> T::Epsilon { T::default_max_relative() @@ -188,6 +219,11 @@ impl<'a, T: RelativeEq + ?Sized> RelativeEq for &'a mut T { } impl RelativeEq for cell::Cell { + #[inline] + fn default_relative_epsilon() -> T::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> T::Epsilon { T::default_max_relative() @@ -205,6 +241,11 @@ impl RelativeEq for cell::Cell { } impl RelativeEq for cell::RefCell { + #[inline] + fn default_relative_epsilon() -> T::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> T::Epsilon { T::default_max_relative() @@ -226,6 +267,11 @@ where A: RelativeEq, A::Epsilon: Clone, { + #[inline] + fn default_relative_epsilon() -> A::Epsilon { + A::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> A::Epsilon { A::default_max_relative() @@ -246,6 +292,11 @@ where A: RelativeEq, A::Epsilon: Clone, { + #[inline] + fn default_relative_epsilon() -> A::Epsilon { + A::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> A::Epsilon { A::default_max_relative() @@ -266,6 +317,11 @@ where A: RelativeEq, A::Epsilon: Clone, { + #[inline] + fn default_relative_epsilon() -> A::Epsilon { + A::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> A::Epsilon { A::default_max_relative() @@ -284,6 +340,10 @@ where macro_rules! impl_relative_eq { () => { impl RelativeEq for () { + fn default_relative_epsilon() -> Self::Epsilon { + () + } + fn default_max_relative() -> Self::Epsilon { () } @@ -305,6 +365,10 @@ macro_rules! impl_relative_eq { where $( []: RelativeEq, )+ { + fn default_relative_epsilon() -> Self::Epsilon { + ($( []::default_relative_epsilon(), )+) + } + fn default_max_relative() -> Self::Epsilon { ($( []::default_max_relative(), )+) } @@ -348,6 +412,11 @@ impl RelativeEq for Complex where T::Epsilon: Clone, { + #[inline] + fn default_relative_epsilon() -> T::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> T::Epsilon { T::default_max_relative() @@ -368,6 +437,11 @@ where #[cfg(feature = "ordered_float")] #[cfg_attr(docsrs, doc(cfg(feature = "ordered_float")))] impl RelativeEq for NotNan { + #[inline] + fn default_relative_epsilon() -> Self::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() @@ -392,6 +466,11 @@ impl RelativeEq for NotNan { #[cfg(feature = "ordered_float")] #[cfg_attr(docsrs, doc(cfg(feature = "ordered_float")))] impl RelativeEq for NotNan { + #[inline] + fn default_relative_epsilon() -> Self::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() @@ -406,6 +485,11 @@ impl RelativeEq for NotNan< #[cfg(feature = "ordered_float")] #[cfg_attr(docsrs, doc(cfg(feature = "ordered_float")))] impl RelativeEq for OrderedFloat { + #[inline] + fn default_relative_epsilon() -> Self::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() @@ -430,6 +514,11 @@ impl RelativeEq for OrderedFlo #[cfg(feature = "ordered_float")] #[cfg_attr(docsrs, doc(cfg(feature = "ordered_float")))] impl RelativeEq for OrderedFloat { + #[inline] + fn default_relative_epsilon() -> Self::Epsilon { + T::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() @@ -451,6 +540,11 @@ where S1: BuildHasher, S2: BuildHasher, { + #[inline] + fn default_relative_epsilon() -> V1::Epsilon { + V1::default_relative_epsilon() + } + #[inline] fn default_max_relative() -> V1::Epsilon { V1::default_max_relative() diff --git a/approx/tests/relative_eq.rs b/approx/tests/relative_eq.rs index ea583ea..56adf56 100644 --- a/approx/tests/relative_eq.rs +++ b/approx/tests/relative_eq.rs @@ -76,18 +76,20 @@ mod test_f32 { #[test] fn test_small() { - assert_relative_eq!(0.000010001f32, 0.000010002f32); - assert_relative_eq!(0.000010002f32, 0.000010001f32); - assert_relative_ne!(0.000001002f32, 0.0000001001f32); - assert_relative_ne!(0.000001001f32, 0.0000001002f32); + assert_ne!(1e-20f32, 1.00000005e-20f32); + assert_relative_eq!(1e-20f32, 1.00000005e-20f32); + assert_relative_eq!(1.00000005e-20f32, 1e-20f32); + assert_relative_ne!(1e-20f32, 1.0000001e-20f32); + assert_relative_ne!(1.0000001e-20f32, 1e-20f32); } #[test] fn test_small_neg() { - assert_relative_eq!(-0.000010001f32, -0.000010002f32); - assert_relative_eq!(-0.000010002f32, -0.000010001f32); - assert_relative_ne!(-0.000001002f32, -0.0000001001f32); - assert_relative_ne!(-0.000001001f32, -0.0000001002f32); + assert_ne!(-1e-20f32, -1.00000005e-20f32); + assert_relative_eq!(-1e-20f32, -1.00000005e-20f32); + assert_relative_eq!(-1.00000005e-20f32, -1e-20f32); + assert_relative_ne!(-1e-20f32, -1.0000001e-20f32); + assert_relative_ne!(-1.0000001e-20f32, -1e-20f32); } #[test] @@ -104,15 +106,15 @@ mod test_f32 { #[test] fn test_epsilon() { - assert_relative_eq!(0.0f32, 1e-40f32, epsilon = 1e-40f32); - assert_relative_eq!(1e-40f32, 0.0f32, epsilon = 1e-40f32); - assert_relative_eq!(0.0f32, -1e-40f32, epsilon = 1e-40f32); - assert_relative_eq!(-1e-40f32, 0.0f32, epsilon = 1e-40f32); + assert_relative_eq!(0.0f32, 1e-20f32, epsilon = 1e-20f32); + assert_relative_eq!(1e-20f32, 0.0f32, epsilon = 1e-20f32); + assert_relative_eq!(0.0f32, -1e-20f32, epsilon = 1e-20f32); + assert_relative_eq!(-1e-20f32, 0.0f32, epsilon = 1e-20f32); - assert_relative_ne!(1e-40f32, 0.0f32, epsilon = 1e-41f32); - assert_relative_ne!(0.0f32, 1e-40f32, epsilon = 1e-41f32); - assert_relative_ne!(-1e-40f32, 0.0f32, epsilon = 1e-41f32); - assert_relative_ne!(0.0f32, -1e-40f32, epsilon = 1e-41f32); + assert_relative_ne!(1e-20f32, 0.0f32, epsilon = 1e-21f32); + assert_relative_ne!(0.0f32, 1e-20f32, epsilon = 1e-21f32); + assert_relative_ne!(-1e-20f32, 0.0f32, epsilon = 1e-21f32); + assert_relative_ne!(0.0f32, -1e-20f32, epsilon = 1e-21f32); } #[test] @@ -169,15 +171,15 @@ mod test_f32 { assert_relative_ne!(-1.0f32, 1.000000001f32); assert_relative_ne!(-1.000000001f32, 1.0f32); assert_relative_ne!(1.0f32, -1.000000001f32); - - assert_relative_eq!(10.0 * f32::MIN_POSITIVE, 10.0 * -f32::MIN_POSITIVE); } #[test] fn test_close_to_zero() { assert_relative_eq!(f32::MIN_POSITIVE, f32::MIN_POSITIVE); - assert_relative_eq!(f32::MIN_POSITIVE, -f32::MIN_POSITIVE); - assert_relative_eq!(-f32::MIN_POSITIVE, f32::MIN_POSITIVE); + assert_relative_eq!(f32::MIN_POSITIVE / 2.0f32, -f32::MIN_POSITIVE / 2.0f32); + assert_relative_eq!(-f32::MIN_POSITIVE / 2.0f32, f32::MIN_POSITIVE / 2.0f32); + assert_relative_ne!(f32::MIN_POSITIVE, -f32::MIN_POSITIVE); + assert_relative_ne!(-f32::MIN_POSITIVE, f32::MIN_POSITIVE); assert_relative_eq!(f32::MIN_POSITIVE, 0.0f32); assert_relative_eq!(0.0f32, f32::MIN_POSITIVE); @@ -260,18 +262,20 @@ mod test_f64 { #[test] fn test_small() { - assert_relative_eq!(0.0000000100000001f64, 0.0000000100000002f64); - assert_relative_eq!(0.0000000100000002f64, 0.0000000100000001f64); - assert_relative_ne!(0.0000000100000001f64, 0.0000000010000002f64); - assert_relative_ne!(0.0000000100000002f64, 0.0000000010000001f64); + assert_ne!(1e-250f64, 1.0000000000000002e-250f64); + assert_relative_eq!(1e-250f64, 1.0000000000000002e-250f64); + assert_relative_eq!(1.0000000000000002e-250f64, 1e-250f64); + assert_relative_ne!(1e-250f64, 1.0000000000000005e-250f64); + assert_relative_ne!(1.0000000000000005e-250f64, 1e-250f64); } #[test] fn test_small_neg() { - assert_relative_eq!(-0.0000000100000001f64, -0.0000000100000002f64); - assert_relative_eq!(-0.0000000100000002f64, -0.0000000100000001f64); - assert_relative_ne!(-0.0000000100000001f64, -0.0000000010000002f64); - assert_relative_ne!(-0.0000000100000002f64, -0.0000000010000001f64); + assert_ne!(-1e-250f64, -1.0000000000000002e-250f64); + assert_relative_eq!(-1e-250f64, -1.0000000000000002e-250f64); + assert_relative_eq!(-1.0000000000000002e-250f64, -1e-250f64); + assert_relative_ne!(-1e-250f64, -1.0000000000000005e-250f64); + assert_relative_ne!(-1.0000000000000005e-250f64, -1e-250f64); } #[test] @@ -337,6 +341,12 @@ mod test_f64 { assert_relative_ne!(f64::NEG_INFINITY, f64::INFINITY); } + #[test] + fn test_zero_infinity() { + assert_relative_ne!(0f64, f64::INFINITY); + assert_relative_ne!(0f64, f64::NEG_INFINITY); + } + #[test] fn test_nan() { assert_relative_ne!(f64::NAN, f64::NAN); @@ -364,19 +374,19 @@ mod test_f64 { #[test] fn test_opposite_signs() { - assert_relative_ne!(1.000000001f64, -1.0f64); - assert_relative_ne!(-1.0f64, 1.000000001f64); - assert_relative_ne!(-1.000000001f64, 1.0f64); - assert_relative_ne!(1.0f64, -1.000000001f64); - - assert_relative_eq!(10.0 * f64::MIN_POSITIVE, 10.0 * -f64::MIN_POSITIVE); + assert_relative_ne!(1.0000000000000002f64, -1.0f64); + assert_relative_ne!(-1.0f64, 1.0000000000000002f64); + assert_relative_ne!(-1.0000000000000002f64, 1.0f64); + assert_relative_ne!(1.0f64, -1.0000000000000002f64); } #[test] fn test_close_to_zero() { assert_relative_eq!(f64::MIN_POSITIVE, f64::MIN_POSITIVE); - assert_relative_eq!(f64::MIN_POSITIVE, -f64::MIN_POSITIVE); - assert_relative_eq!(-f64::MIN_POSITIVE, f64::MIN_POSITIVE); + assert_relative_eq!(f64::MIN_POSITIVE / 2.0f64, -f64::MIN_POSITIVE / 2.0f64); + assert_relative_eq!(-f64::MIN_POSITIVE / 2.0f64, f64::MIN_POSITIVE / 2.0f64); + assert_relative_ne!(f64::MIN_POSITIVE, -f64::MIN_POSITIVE); + assert_relative_ne!(-f64::MIN_POSITIVE, f64::MIN_POSITIVE); assert_relative_eq!(f64::MIN_POSITIVE, 0.0f64); assert_relative_eq!(0.0f64, f64::MIN_POSITIVE);