From d10487a84df9521884acce3945144a8ef0dd2957 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Tue, 15 Aug 2023 09:51:43 -0400 Subject: [PATCH 1/2] improve lib.rs examples, add tests --- src/lib.rs | 30 ++++++++++++++++++------------ tests/abs_diff_eq.rs | 24 ++++++++++++++++++++++++ tests/relative_eq.rs | 18 ++++++++++++++++++ tests/ulps_eq.rs | 5 +++++ 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4730864..7248e78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,23 +26,29 @@ //! use std::f64; //! //! # fn main() { -//! abs_diff_eq!(1.0, 1.0); -//! abs_diff_eq!(1.0, 1.0, epsilon = f64::EPSILON); +//! static ε: f64 = f64::EPSILON; +//! assert_abs_diff_eq!(1.0, 1.0); // ✅ +//! assert_abs_diff_eq!(1.0, 1.0 + ε); // ✅ default: epsilon = f64::EPSILON +//! assert_abs_diff_ne!(1.0, 1.0 + ε+ε); // ❌ diff (2ε) exceeds default (ε); assert "ne" instead of "eq" +//! assert_abs_diff_eq!(1.0, 1.0 + ε+ε, epsilon = ε+ε); // ✅ diff (2ε) ≤ "epsilon" param (2ε) //! -//! relative_eq!(1.0, 1.0); -//! relative_eq!(1.0, 1.0, epsilon = f64::EPSILON); -//! relative_eq!(1.0, 1.0, max_relative = 1.0); -//! relative_eq!(1.0, 1.0, epsilon = f64::EPSILON, max_relative = 1.0); -//! relative_eq!(1.0, 1.0, max_relative = 1.0, epsilon = f64::EPSILON); +//! assert_relative_eq!(1.0, 1.0); // ✅ compare abs(a - b) / max(a, b) to default (f64::EPSILON) +//! assert_relative_ne!(1.0, 1.1); // ❌ 0.1/1.1 ≥ ε (assert "ne" instead of "eq") +//! assert_relative_eq!(1.0, 1.1, max_relative = 0.1); // ✅ 0.1/1.1 < 0.1 +//! assert_relative_eq!(1.1, 1.0, max_relative = 0.1); // ✅ order doesn't matter, cmp is commutative +//! assert_relative_ne!(1.0, 1.2, max_relative = 0.1); // ❌ 0.2/1.2 > 0.1 +//! assert_relative_ne!(0.0, 1e-6, max_relative = 1e-5); // ❌ maximum possible relative diff is 1.0 (when one side is 0) +//! assert_relative_eq!(0.0, 1e-6, epsilon = 1e-5, max_relative = 1e-5); // ✅ passing `epsilon` allows short-circuiting based on small abs diff //! -//! ulps_eq!(1.0, 1.0); -//! ulps_eq!(1.0, 1.0, epsilon = f64::EPSILON); -//! ulps_eq!(1.0, 1.0, max_ulps = 4); -//! ulps_eq!(1.0, 1.0, epsilon = f64::EPSILON, max_ulps = 4); -//! ulps_eq!(1.0, 1.0, max_ulps = 4, epsilon = f64::EPSILON); +//! assert_ulps_eq!(1., 1. + 1e-17); // ✅ default: max_ulps = 4 +//! assert_ulps_eq!(1., 1. + 1e-16); // ✅ "" +//! assert_ulps_ne!(1., 1. + 1e-15); // ❌ assert "ne" instead of "eq" +//! assert_ulps_eq!(1., 1. + 1e-15, max_ulps = 5); // ✅ relaxed max_ulps //! # } //! ``` //! +//! See also the [`abs_diff_eq!`](AbsDiffEq::abs_diff_eq), [`relative_eq!`](RelativeEq::relative_eq) and [`ulps_eq!`](UlpsEq::ulps_eq) macros, which return [`bool`] instead of [`assert`]ing. +//! //! # Implementing approximate equality for custom types //! //! The `*Eq` traits allow approximate equalities to be implemented on types, based on the diff --git a/tests/abs_diff_eq.rs b/tests/abs_diff_eq.rs index 90e8f1a..dbee38c 100644 --- a/tests/abs_diff_eq.rs +++ b/tests/abs_diff_eq.rs @@ -22,6 +22,8 @@ extern crate approx; mod test_f32 { use core::f32; + static ε: f32 = f32::EPSILON; + #[test] fn test_basic() { assert_abs_diff_eq!(1.0f32, 1.0f32); @@ -100,6 +102,16 @@ mod test_f32 { assert_abs_diff_ne!(0.0f32, -0.000001f32); } + #[test] + fn test_default_epsilon() { + assert_abs_diff_eq!(1.0f32, 1.0f32 + ε); + assert_abs_diff_ne!(1.0f32, 1.0f32 + ε + ε); + assert_abs_diff_eq!(1.0f32, 1.0f32 + ε + ε, epsilon = ε + ε); + assert_abs_diff_eq!(1.0f32, 1.0f32 - ε); + assert_abs_diff_ne!(1.0f32, 1.0f32 - ε - ε); + assert_abs_diff_eq!(1.0f32, 1.0f32 - ε - ε, epsilon = ε + ε); + } + #[test] fn test_epsilon() { assert_abs_diff_eq!(0.0f32, 1e-40f32, epsilon = 1e-40f32); @@ -191,6 +203,8 @@ mod test_f32 { mod test_f64 { use core::f64; + static ε: f64 = f64::EPSILON; + #[test] fn test_basic() { assert_abs_diff_eq!(1.0f64, 1.0f64); @@ -282,6 +296,16 @@ mod test_f64 { assert_abs_diff_ne!(0.0f64, -1e-40f64, epsilon = 1e-41f64); } + #[test] + fn test_default_epsilon() { + assert_abs_diff_eq!(1.0, 1.0 + ε); + assert_abs_diff_ne!(1.0, 1.0 + ε + ε); + assert_abs_diff_eq!(1.0, 1.0 + ε + ε, epsilon = ε + ε); + assert_abs_diff_eq!(1.0, 1.0 - ε); + assert_abs_diff_ne!(1.0, 1.0 - ε - ε); + assert_abs_diff_eq!(1.0, 1.0 - ε - ε, epsilon = ε + ε); + } + #[test] fn test_max() { assert_abs_diff_eq!(f64::MAX, f64::MAX); diff --git a/tests/relative_eq.rs b/tests/relative_eq.rs index 3a7629e..ed1b58c 100644 --- a/tests/relative_eq.rs +++ b/tests/relative_eq.rs @@ -193,10 +193,17 @@ mod test_f32 { mod test_f64 { use core::f64; + static ε: f64 = f64::EPSILON; + #[test] fn test_basic() { assert_relative_eq!(1.0f64, 1.0f64); assert_relative_ne!(1.0f64, 2.0f64); + assert_relative_eq!(1.0, 1.0); // ✅ + assert_relative_ne!(1.0, 1.1); // ❌ + assert_relative_eq!(1.0, 1.1, max_relative = 0.1); // ✅ + assert_relative_eq!(1.1, 1.0, max_relative = 0.1); // ✅ order doesn't matter: abs(a - b) / max(a, b) + assert_relative_ne!(1.0, 1.2, max_relative = 0.1); // ❌ } #[test] @@ -284,6 +291,17 @@ mod test_f64 { assert_relative_ne!(0.0f64, -1e-40f64, epsilon = 1e-41f64); } + #[test] + fn test_epsilon_combo() { + assert_relative_eq!(0., ε, epsilon = ε, max_relative = 0.1); + assert_relative_ne!(0., ε + ε, epsilon = ε, max_relative = 0.1); // ❌ abs (2ε > ε) and relative (1. > 0.1) checks both fail + assert_relative_eq!(ε, 4.*ε, epsilon = ε, max_relative = 0.75); // ✅ relative check passes (3ε/4ε == 0.75) + assert_relative_ne!(ε, 4.*ε, epsilon = ε, max_relative = 0.7); // ❌ abs (3ε > ε) and relative (0.75 > 0.7) checks both fail + assert_relative_eq!(ε, 4.*ε, epsilon = 3.*ε, max_relative = 0.7); // ✅ abs (3ε) check passes + assert_relative_ne!(0.0, 1e-6, max_relative = 1e-5); // ❌ maximum possible relative diff is 1.0 (when one side is 0) + assert_relative_eq!(0.0, 1e-6, epsilon = 1e-5, max_relative = 1e-5); // ✅ passing `epsilon` allows short-circuiting based on small abs diff + } + #[test] fn test_max() { assert_relative_eq!(f64::MAX, f64::MAX); diff --git a/tests/ulps_eq.rs b/tests/ulps_eq.rs index b0e89ed..353e255 100644 --- a/tests/ulps_eq.rs +++ b/tests/ulps_eq.rs @@ -193,6 +193,11 @@ mod test_f64 { fn test_basic() { assert_ulps_eq!(1.0f64, 1.0f64); assert_ulps_ne!(1.0f64, 2.0f64); + + assert_ulps_eq!(1., 1. + 1e-17); // ✅ default: max_ulps = 4 + assert_ulps_eq!(1., 1. + 1e-16); // ✅ "" + assert_ulps_ne!(1., 1. + 1e-15); // ❌ + assert_ulps_eq!(1., 1. + 1e-15, max_ulps = 5); // ✅ relaxed max_ulps } #[test] From 39ff2c49144445836895921c76b17c979fc94c7e Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Tue, 15 Aug 2023 10:14:06 -0400 Subject: [PATCH 2/2] README: rm defunct travis badge --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 601cc3c..3d483df 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,15 @@ # approx -[![Build Status][travis_badge]][travis_url] [![Version][version_badge]][crate_url] [![Documentation][docs_badge]][docs_url] [![Downloads][downloads_badge]][crate_url] [![License][license_badge]][license_url] -[travis_badge]: https://travis-ci.org/brendanzab/approx.svg?branch=master [docs_badge]: https://docs.rs/approx/badge.svg [version_badge]: https://img.shields.io/crates/v/approx.svg [license_badge]: https://img.shields.io/crates/l/approx.svg [downloads_badge]: https://img.shields.io/crates/d/approx.svg -[travis_url]: https://travis-ci.org/brendanzab/approx [docs_url]: https://docs.rs/approx [crate_url]: https://crates.io/crates/approx [license_url]: https://github.com/brendanzab/approx/blob/master/LICENSE