#![warn(
missing_docs,
clippy::all,
clippy::correctness,
clippy::suspicious,
clippy::style,
clippy::complexity,
clippy::perf,
clippy::pedantic,
clippy::cargo,
clippy::nursery
)]
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#[doc(hidden)]
pub mod macros;
#[cfg(feature = "half")]
mod half;
#[cfg(feature = "euclid")]
mod euclid;
trait Abs {
fn abs(&self) -> Self;
}
#[cfg(feature = "std")]
mod abs {
impl crate::Abs for f32 {
fn abs(&self) -> Self {
Self::abs(*self)
}
}
impl crate::Abs for f64 {
fn abs(&self) -> Self {
Self::abs(*self)
}
}
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
mod abs {
impl crate::Abs for f32 {
fn abs(&self) -> Self {
libm::fabsf(*self)
}
}
impl crate::Abs for f64 {
fn abs(&self) -> Self {
libm::fabs(*self)
}
}
}
pub trait IsClose<Rhs = Self> {
type Tolerance;
const ZERO_TOL: Self::Tolerance;
const ABS_TOL: Self::Tolerance;
const REL_TOL: Self::Tolerance;
fn is_close_tol(&self, rhs: &Rhs, rel_tol: &Self::Tolerance, abs_tol: &Self::Tolerance)
-> bool;
#[inline]
fn is_close(&self, rhs: &Rhs) -> bool {
self.is_close_tol(rhs, &Self::REL_TOL, &Self::ABS_TOL)
}
#[inline]
fn is_close_rel_tol(&self, rhs: &Rhs, rel_tol: &Self::Tolerance) -> bool {
self.is_close_tol(rhs, rel_tol, &Self::ZERO_TOL)
}
#[inline]
fn is_close_abs_tol(&self, rhs: &Rhs, abs_tol: &Self::Tolerance) -> bool {
self.is_close_tol(rhs, &Self::ZERO_TOL, abs_tol)
}
}
impl IsClose for f32 {
type Tolerance = Self;
const ZERO_TOL: Self = 0.0;
const ABS_TOL: Self = 1e-6;
const REL_TOL: Self = 1e-6;
#[inline]
fn is_close_tol(&self, rhs: &Self, rel_tol: &Self, abs_tol: &Self) -> bool {
let tol = Self::max(Abs::abs(self), Abs::abs(rhs)) * rel_tol + abs_tol;
(*self - *rhs).abs() <= tol
}
}
impl IsClose for f64 {
type Tolerance = Self;
const ZERO_TOL: Self = 0.0;
const ABS_TOL: Self = 1e-9;
const REL_TOL: Self = 1e-9;
#[inline]
fn is_close_tol(&self, rhs: &Self, rel_tol: &Self, abs_tol: &Self) -> bool {
let tol = Self::max(Abs::abs(self), Abs::abs(rhs)) * rel_tol + abs_tol;
(*self - *rhs).abs() <= tol
}
}
impl<Typ, Tol> IsClose<Typ> for &Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &Typ, rel_tol: &Tol, abs_tol: &Tol) -> bool {
(**self).is_close_tol(rhs, rel_tol, abs_tol)
}
}
impl<Typ, Tol> IsClose<Typ> for &mut Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &Typ, rel_tol: &Tol, abs_tol: &Tol) -> bool {
(**self).is_close_tol(rhs, rel_tol, abs_tol)
}
}
impl<Typ, Tol> IsClose<&Typ> for Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &&Typ, rel_tol: &Tol, abs_tol: &Tol) -> bool {
self.is_close_tol(*rhs, rel_tol, abs_tol)
}
}
impl<Typ, Tol> IsClose<&mut Typ> for Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &&mut Typ, rel_tol: &Tol, abs_tol: &Tol) -> bool {
self.is_close_tol(*rhs, rel_tol, abs_tol)
}
}
impl<Typ, Tol> IsClose for &Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &Self, rel_tol: &Tol, abs_tol: &Tol) -> bool {
(**self).is_close_tol(*rhs, rel_tol, abs_tol)
}
}
impl<Typ, Tol> IsClose<&Typ> for &mut Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &&Typ, rel_tol: &Tol, abs_tol: &Tol) -> bool {
(**self).is_close_tol(*rhs, rel_tol, abs_tol)
}
}
impl<Typ, Tol> IsClose<&mut Typ> for &Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &&mut Typ, rel_tol: &Tol, abs_tol: &Tol) -> bool {
(**self).is_close_tol(*rhs, rel_tol, abs_tol)
}
}
impl<Typ, Tol> IsClose for &mut Typ
where
Typ: IsClose<Tolerance = Tol>,
{
type Tolerance = Tol;
const ZERO_TOL: Tol = <Typ as IsClose>::ZERO_TOL;
const ABS_TOL: Tol = <Typ as IsClose>::ABS_TOL;
const REL_TOL: Tol = <Typ as IsClose>::REL_TOL;
#[inline]
fn is_close_tol(&self, rhs: &Self, rel_tol: &Tol, abs_tol: &Tol) -> bool {
(**self).is_close_tol(*rhs, rel_tol, abs_tol)
}
}
#[cfg(test)]
mod tests {
use std::f32::consts::PI as PI_F32;
use std::f64::consts::PI as PI_F64;
use super::*;
#[test]
fn abs_trait() {
let abs = Abs::abs(&1.0_f32);
assert!((-f32::EPSILON..f32::EPSILON).contains(&(abs - 1.0)));
let abs = Abs::abs(&-1.0_f32);
assert!((-f32::EPSILON..f32::EPSILON).contains(&(abs - 1.0)));
let abs = Abs::abs(&1.0_f64);
assert!((-f64::EPSILON..f64::EPSILON).contains(&(abs - 1.0)));
let abs = Abs::abs(&-1.0_f64);
assert!((-f64::EPSILON..f64::EPSILON).contains(&(abs - 1.0)));
}
#[test]
fn default_is_close() {
assert!(PI_F32.is_close(&(355.0 / 113.0)));
assert!(!PI_F32.is_close(&(22.0 / 7.0)));
}
#[test]
fn default_is_close_tol() {
assert!(1.0.is_close_tol(&(1.0 + 1e-2), &1e-1, &0.0));
assert!(!1e-2.is_close_tol(&(1e-2 + 1e-2), &1e-1, &0.0));
assert!(1e-2.is_close_tol(&(1e-2 + 1e-2), &0.0, &1e-1));
assert!(!1.0.is_close_tol(&(1.0 + 1.0), &0.0, &1e-1));
}
#[test]
fn default_is_close_rel_tol() {
assert!(1.0.is_close_rel_tol(&(1.0 + 1e-2), &1e-1));
assert!(!1e-2.is_close_rel_tol(&(1e-2 + 1e-2), &1e-1));
}
#[test]
fn default_is_close_abs_tol() {
assert!(1e-2.is_close_abs_tol(&(1e-2 + 1e-2), &1e-1));
assert!(!1.0.is_close_abs_tol(&(1.0 + 1.0), &1e-1));
}
#[test]
fn f32_is_close_tol() {
assert!(PI_F32.is_close_tol(&(22.0 / 7.0), &1e-2, &1e-2));
assert!(!PI_F32.is_close_tol(&(22.0 / 7.0), &1e-5, &1e-5));
}
#[test]
fn f64_is_close_tol() {
assert!(PI_F64.is_close_tol(&(22.0 / 7.0), &1e-2, &1e-2));
assert!(!PI_F64.is_close_tol(&(22.0 / 7.0), &1e-5, &1e-5));
}
#[test]
fn ref_is_close_tol() {
assert!(<&f32 as IsClose<f32>>::is_close(&&PI_F32, &(355.0 / 113.0)));
assert!(!<&f32 as IsClose<f32>>::is_close(&&PI_F32, &(22.0 / 7.0)));
let mut pi_f32 = PI_F32;
assert!(<&mut f32 as IsClose<f32>>::is_close(
&&mut pi_f32,
&(355.0 / 113.0)
));
assert!(!<&mut f32 as IsClose<f32>>::is_close(
&&mut pi_f32,
&(22.0 / 7.0)
));
assert!(<f32 as IsClose<&f32>>::is_close(&PI_F32, &&(355.0 / 113.0)));
assert!(!<f32 as IsClose<&f32>>::is_close(&PI_F32, &&(22.0 / 7.0)));
assert!(<f32 as IsClose<&mut f32>>::is_close(
&PI_F32,
&&mut (355.0 / 113.0)
));
assert!(!<f32 as IsClose<&mut f32>>::is_close(
&PI_F32,
&&mut (22.0 / 7.0)
));
assert!(<&f32 as IsClose<&f32>>::is_close(
&&PI_F32,
&&(355.0 / 113.0)
));
assert!(!<&f32 as IsClose<&f32>>::is_close(&&PI_F32, &&(22.0 / 7.0)));
let mut pi_f32 = PI_F32;
assert!(<&mut f32 as IsClose<&f32>>::is_close(
&&mut pi_f32,
&&(355.0 / 113.0)
));
assert!(!<&mut f32 as IsClose<&f32>>::is_close(
&&mut pi_f32,
&&(22.0 / 7.0)
));
assert!(<&f32 as IsClose<&mut f32>>::is_close(
&&PI_F32,
&&mut (355.0 / 113.0)
));
assert!(!<&f32 as IsClose<&mut f32>>::is_close(
&&PI_F32,
&&mut (22.0 / 7.0)
));
let mut pi_f32 = PI_F32;
assert!(<&mut f32 as IsClose<&mut f32>>::is_close(
&&mut pi_f32,
&&mut (355.0 / 113.0)
));
assert!(!<&mut f32 as IsClose<&mut f32>>::is_close(
&&mut pi_f32,
&&mut (22.0 / 7.0)
));
}
}