#![warn(missing_docs)]
pub trait FloatDiff {
type AbsDiff;
type UlpsDiff;
fn abs_diff(&self, other: &Self) -> Self::AbsDiff;
fn ulps_diff(&self, other: &Self) -> Self::UlpsDiff;
}
pub trait FloatEq {
type DiffEpsilon;
type UlpsDiffEpsilon;
fn eq_abs(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> bool;
fn ne_abs(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> bool {
!self.eq_abs(other, max_diff)
}
fn eq_rel(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> bool;
fn ne_rel(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> bool {
!self.eq_rel(other, max_diff)
}
fn eq_ulps(&self, other: &Self, max_diff: &Self::UlpsDiffEpsilon) -> bool;
fn ne_ulps(&self, other: &Self, max_diff: &Self::UlpsDiffEpsilon) -> bool {
!self.eq_ulps(other, max_diff)
}
fn rel_epsilon(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> Self::DiffEpsilon;
}
#[macro_export]
macro_rules! float_eq {
($a:expr, $b:expr, $($eq:ident <= $max_diff:expr),+) => ({
match (&$a, &$b) {
(a_val, b_val) => {
false $(|| $crate::FloatEqCmp::$eq(a_val, &b_val, &$max_diff))+
}
}
});
($a:expr, $b:expr, $($eq:ident <= $max_diff:expr),+,) => ({
$crate::float_eq!($a, $b $(, $eq <= $max_diff)+)
})
}
#[macro_export]
macro_rules! float_ne {
($a:expr, $b:expr, $($eq:ident <= $max_diff:expr),+) => ({
!$crate::float_eq!($a, $b $(, $eq <= $max_diff)+)
});
($a:expr, $b:expr, $($eq:ident <= $max_diff:expr),+,) => ({
!$crate::float_eq!($a, $b $(, $eq <= $max_diff)+)
});
}
#[macro_export]
macro_rules! assert_float_eq {
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr, $eq3:ident <= $max_diff_3:expr) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2, &$max_diff_3) {
(left_val, right_val, max_diff_1_val, max_diff_2_val, max_diff_3_val) => {
if !$crate::float_eq!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val,
$eq3 <= *max_diff_3_val) {
panic!(concat!(
"assertion failed: `float_eq!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), " <= ε, ", stringify!($eq3), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
concat!("[", stringify!($eq3), "]"),
$crate::FloatCmpDiffName::$eq3(),
$crate::FloatCmpOpEpsilon::$eq3(left_val, right_val, &*max_diff_3_val)
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2) {
(left_val, right_val, max_diff_1_val, max_diff_2_val) => {
if !$crate::float_eq!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val) {
panic!(concat!(
"assertion failed: `float_eq!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr) => ({
match (&$left, &$right, &$max_diff_1) {
(left_val, right_val, max_diff_1_val) => {
if !$crate::float_eq!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val) {
panic!(concat!(
"assertion failed: `float_eq!(left, right, ", stringify!($eq1), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
)
}
}
}
});
($left:expr, $right:expr, $($eq:ident <= $max_diff:expr,)+) => ({
$crate::assert_float_eq!($left, $right $(, $eq <= $max_diff)+)
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr, $eq3:ident <= $max_diff_3:expr, $($arg:tt)+) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2, &$max_diff_3) {
(left_val, right_val, max_diff_1_val, max_diff_2_val, max_diff_3_val) => {
if !$crate::float_eq!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val,
$eq3 <= *max_diff_3_val) {
panic!(concat!(
"assertion failed: `float_eq!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), " <= ε, ", stringify!($eq3), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`: {}"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
concat!("[", stringify!($eq3), "]"),
$crate::FloatCmpDiffName::$eq3(),
$crate::FloatCmpOpEpsilon::$eq3(left_val, right_val, &*max_diff_3_val),
format_args!($($arg)+)
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr, $($arg:tt)+) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2) {
(left_val, right_val, max_diff_1_val, max_diff_2_val) => {
if !$crate::float_eq!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val) {
panic!(concat!(
"assertion failed: `float_eq!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`: {}"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
format_args!($($arg)+)
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $($arg:tt)+) => ({
match (&$left, &$right, &$max_diff_1) {
(left_val, right_val, max_diff_1_val) => {
if !$crate::float_eq!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val) {
panic!(concat!(
"assertion failed: `float_eq!(left, right, ", stringify!($eq1), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`: {}"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
format_args!($($arg)+)
)
}
}
}
});
}
#[macro_export]
macro_rules! assert_float_ne {
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr, $eq3:ident <= $max_diff_3:expr) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2, &$max_diff_3) {
(left_val, right_val, max_diff_1_val, max_diff_2_val, max_diff_3_val) => {
if !$crate::float_ne!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val,
$eq3 <= *max_diff_3_val) {
panic!(concat!(
"assertion failed: `float_ne!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), " <= ε, ", stringify!($eq3), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
concat!("[", stringify!($eq3), "]"),
$crate::FloatCmpDiffName::$eq3(),
$crate::FloatCmpOpEpsilon::$eq3(left_val, right_val, &*max_diff_3_val)
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2) {
(left_val, right_val, max_diff_1_val, max_diff_2_val) => {
if !$crate::float_ne!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val) {
panic!(concat!(
"assertion failed: `float_ne!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr) => ({
match (&$left, &$right, &$max_diff_1) {
(left_val, right_val, max_diff_1_val) => {
if !$crate::float_ne!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val) {
// The reborrows below are intentional. See assert_eq! in the standard library.
panic!(concat!(
"assertion failed: `float_ne!(left, right, ", stringify!($eq1), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
)
}
}
}
});
($left:expr, $right:expr, $($eq:ident <= $max_diff:expr,)+) => ({
$crate::assert_float_ne!($left, $right $(, $eq <= $max_diff)+)
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr, $eq3:ident <= $max_diff_3:expr, $($arg:tt)+) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2, &$max_diff_3) {
(left_val, right_val, max_diff_1_val, max_diff_2_val, max_diff_3_val) => {
if !$crate::float_ne!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val,
$eq3 <= *max_diff_3_val) {
// The reborrows below are intentional. See assert_eq! in the standard library.
panic!(concat!(
"assertion failed: `float_ne!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), " <= ε, ", stringify!($eq3), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`: {}"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
concat!("[", stringify!($eq3), "]"),
$crate::FloatCmpDiffName::$eq3(),
$crate::FloatCmpOpEpsilon::$eq3(left_val, right_val, &*max_diff_3_val),
format_args!($($arg)+)
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $eq2:ident <= $max_diff_2:expr, $($arg:tt)+) => ({
match (&$left, &$right, &$max_diff_1, &$max_diff_2) {
(left_val, right_val, max_diff_1_val, max_diff_2_val) => {
if !$crate::float_ne!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val,
$eq2 <= *max_diff_2_val) {
// The reborrows below are intentional. See assert_eq! in the standard library.
panic!(concat!(
"assertion failed: `float_ne!(left, right, ", stringify!($eq1), " <= ε, ", stringify!($eq2), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`,
{:>9}: {:<9} <= `{:?}`: {}"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
concat!("[", stringify!($eq2), "]"),
$crate::FloatCmpDiffName::$eq2(),
$crate::FloatCmpOpEpsilon::$eq2(left_val, right_val, &*max_diff_2_val),
format_args!($($arg)+)
)
}
}
}
});
($left:expr, $right:expr, $eq1:ident <= $max_diff_1:expr, $($arg:tt)+) => ({
match (&$left, &$right, &$max_diff_1) {
(left_val, right_val, max_diff_1_val) => {
if !$crate::float_ne!(
*left_val,
*right_val,
$eq1 <= *max_diff_1_val) {
// The reborrows below are intentional. See assert_eq! in the standard library.
panic!(concat!(
"assertion failed: `float_ne!(left, right, ", stringify!($eq1), r#" <= ε)`
left: `{:?}`,
right: `{:?}`,
abs_diff: `{:?}`,
ulps_diff: `{:?}`,
{:>9}: {:<9} <= `{:?}`: {}"#),
&*left_val,
&*right_val,
$crate::FloatDiff::abs_diff(&*left_val, &right_val),
$crate::FloatDiff::ulps_diff(&*left_val, &right_val),
concat!("[", stringify!($eq1), "]"),
$crate::FloatCmpDiffName::$eq1(),
$crate::FloatCmpOpEpsilon::$eq1(left_val, right_val, &*max_diff_1_val),
format_args!($($arg)+)
)
}
}
}
});
}
#[macro_export]
macro_rules! debug_assert_float_eq {
($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::assert_float_eq!($($arg)*); })
}
#[macro_export]
macro_rules! debug_assert_float_ne {
($($arg:tt)*) => (if cfg!(debug_assertions) { $crate::assert_float_ne!($($arg)*); })
}
#[doc(hidden)]
pub struct FloatEqCmp;
#[doc(hidden)]
impl FloatEqCmp {
#[inline]
pub fn abs<T: FloatEq>(a: &T, b: &T, max_diff: &T::DiffEpsilon) -> bool {
FloatEq::eq_abs(a, b, max_diff)
}
#[inline]
pub fn rel<T: FloatEq>(a: &T, b: &T, max_diff: &T::DiffEpsilon) -> bool {
FloatEq::eq_rel(a, b, max_diff)
}
#[inline]
pub fn ulps<T: FloatEq>(a: &T, b: &T, max_diff: &T::UlpsDiffEpsilon) -> bool {
FloatEq::eq_ulps(a, b, max_diff)
}
}
#[doc(hidden)]
pub struct FloatCmpDiffName;
#[doc(hidden)]
impl FloatCmpDiffName {
#[inline]
pub const fn abs() -> &'static str {
"abs_diff"
}
#[inline]
pub const fn rel() -> &'static str {
"abs_diff"
}
#[inline]
pub const fn ulps() -> &'static str {
"ulps_diff"
}
}
#[doc(hidden)]
pub struct FloatCmpOpEpsilon;
#[doc(hidden)]
impl FloatCmpOpEpsilon {
#[inline]
pub fn abs<'a, T: FloatEq>(_: &T, _: &T, max_diff: &'a T::DiffEpsilon) -> &'a T::DiffEpsilon {
max_diff
}
#[inline]
pub fn rel<T: FloatEq>(a: &T, b: &T, max_diff: &T::DiffEpsilon) -> T::DiffEpsilon {
FloatEq::rel_epsilon(a, b, max_diff)
}
#[inline]
pub fn ulps<'a, T: FloatEq>(
_: &T,
_: &T,
max_diff: &'a T::UlpsDiffEpsilon,
) -> &'a T::UlpsDiffEpsilon {
max_diff
}
}
macro_rules! impl_traits {
($float:ident, $uint:ident) => {
impl FloatDiff for $float {
type AbsDiff = $float;
type UlpsDiff = $uint;
#[inline]
fn abs_diff(&self, other: &Self) -> Self::AbsDiff {
(self - other).abs()
}
#[inline]
fn ulps_diff(&self, other: &Self) -> Self::UlpsDiff {
let a = (self.to_bits()) as $uint;
let b = (other.to_bits()) as $uint;
let max = a.max(b);
let min = a.min(b);
max - min
}
}
impl FloatEq for $float {
type DiffEpsilon = $float;
type UlpsDiffEpsilon = $uint;
#[inline]
fn eq_abs(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> bool {
self.abs_diff(other).le(max_diff)
}
#[inline]
fn rel_epsilon(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> Self::DiffEpsilon {
self.abs().max(other.abs()) * max_diff
}
#[inline]
fn eq_rel(&self, other: &Self, max_diff: &Self::DiffEpsilon) -> bool {
self.abs_diff(other) <= self.rel_epsilon(other, max_diff)
}
#[inline]
fn eq_ulps(&self, other: &Self, max_diff: &Self::UlpsDiffEpsilon) -> bool {
if self.is_sign_positive() != other.is_sign_positive() {
self == other } else {
self.ulps_diff(other).le(max_diff)
}
}
}
};
}
impl_traits!(f32, u32);
impl_traits!(f64, u64);