#![no_std]
use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
const U32_SIGN_MASK: u32 = 0x8000_0000;
const U32_EXPONENT_MASK: u32 = 0x7F80_0000;
const U32_HIDDEN_BIT: u32 = 0x0080_0000;
const U32_SIGNIFICAND_MASK: u32 = 0x007F_FFFF;
const U32_INFINITY: u32 = 0x7F80_0000;
#[inline]
#[doc(hidden)]
pub fn is_denormal_f32(f: f32) -> bool {
let u = f.to_bits();
(u & U32_EXPONENT_MASK) == 0
}
#[inline]
#[doc(hidden)]
pub fn sign_f32(f: f32) -> i32 {
let u = f.to_bits();
if (u & U32_SIGN_MASK) == 0 {
1
} else {
-1
}
}
#[inline]
#[doc(hidden)]
pub fn significand_f32(f: f32) -> u32 {
let u = f.to_bits();
let s = u & U32_SIGNIFICAND_MASK;
if is_denormal_f32(f) {
s
} else {
s + U32_HIDDEN_BIT
}
}
#[inline]
#[doc(hidden)]
pub fn next_f32(f: f32) -> f32 {
let u = f.to_bits();
if u == U32_INFINITY {
f32::from_bits(U32_INFINITY)
} else if sign_f32(f) < 0 && significand_f32(f) == 0 {
0.0
} else if sign_f32(f) < 0 {
f32::from_bits(u - 1)
} else {
f32::from_bits(u + 1)
}
}
#[inline]
#[doc(hidden)]
pub fn next_n_f32(mut f: f32, n: u32) -> f32 {
for _ in 0..n {
f = next_f32(f);
}
f
}
#[inline]
#[doc(hidden)]
pub fn previous_f32(f: f32) -> f32 {
let u = f.to_bits();
if u == (U32_INFINITY | U32_SIGN_MASK) {
-f32::from_bits(U32_INFINITY)
} else if sign_f32(f) < 0 {
f32::from_bits(u + 1)
} else if significand_f32(f) == 0 {
-0.0
} else {
f32::from_bits(u - 1)
}
}
#[inline]
#[doc(hidden)]
pub fn previous_n_f32(mut f: f32, n: u32) -> f32 {
for _ in 0..n {
f = previous_f32(f);
}
f
}
const U64_SIGN_MASK: u64 = 0x8000_0000_0000_0000;
const U64_EXPONENT_MASK: u64 = 0x7FF0_0000_0000_0000;
const U64_HIDDEN_BIT: u64 = 0x0010_0000_0000_0000;
const U64_SIGNIFICAND_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
const U64_INFINITY: u64 = 0x7FF0_0000_0000_0000;
#[inline]
#[doc(hidden)]
pub fn is_denormal_f64(f: f64) -> bool {
let u = f.to_bits();
(u & U64_EXPONENT_MASK) == 0
}
#[inline]
#[doc(hidden)]
pub fn sign_f64(f: f64) -> i32 {
let u = f.to_bits();
if (u & U64_SIGN_MASK) == 0 {
1
} else {
-1
}
}
#[inline]
#[doc(hidden)]
pub fn significand_f64(f: f64) -> u64 {
let u = f.to_bits();
let s = u & U64_SIGNIFICAND_MASK;
if is_denormal_f64(f) {
s
} else {
s + U64_HIDDEN_BIT
}
}
#[inline]
#[doc(hidden)]
pub fn next_f64(f: f64) -> f64 {
let u = f.to_bits();
if u == U64_INFINITY {
f64::from_bits(U64_INFINITY)
} else if sign_f64(f) < 0 && significand_f64(f) == 0 {
0.0
} else if sign_f64(f) < 0 {
f64::from_bits(u - 1)
} else {
f64::from_bits(u + 1)
}
}
#[inline]
#[doc(hidden)]
pub fn next_n_f64(mut f: f64, n: u32) -> f64 {
for _ in 0..n {
f = next_f64(f);
}
f
}
#[inline]
#[doc(hidden)]
pub fn previous_f64(f: f64) -> f64 {
let u = f.to_bits();
if u == (U64_INFINITY | U64_SIGN_MASK) {
-f64::from_bits(U64_INFINITY)
} else if sign_f64(f) < 0 {
f64::from_bits(u + 1)
} else if significand_f64(f) == 0 {
-0.0
} else {
f64::from_bits(u - 1)
}
}
#[inline]
#[doc(hidden)]
pub fn previous_n_f64(mut f: f64, n: u32) -> f64 {
for _ in 0..n {
f = previous_f64(f);
}
f
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_absolute_eq_error_msg {
() => {
"assertion failed: `|a-b| < epsilon` a: {:?}, b: {:?}, epsilon: {:?}"
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_absolute_ne_error_msg {
() => {
"assertion failed: `|a-b| >= epsilon` a: {:?}, b: {:?}, epsilon: {:?}"
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_relative_eq_error_msg {
() => {
"assertion failed: `|(a-b) / a| < epsilon` a: {:?}, b: {:?}, epsilon: {:?}"
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_relative_ne_error_msg {
() => {
"assertion failed: `|(a-b) / a| >= epsilon` a: {:?}, b: {:?}, epsilon: {:?}"
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_near_error_msg {
() => ("assertion failed: `b is outside of n steps from a` a: {:?}, b: {:?}, n: {:?}, previous: {:?}, next: {:?}")
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_far_error_msg {
() => ("assertion failed: `b is within n steps from a` a: {:?}, b: {:?}, n: {:?}, previous: {:?}, next: {:?}")
}
#[doc(hidden)]
macro_rules! threshold_error_impl {
($t:ident, $msg:expr) => {
#[derive(Debug)]
#[doc(hidden)]
pub struct $t<Float: Debug> {
a: Float,
b: Float,
epsilon: Float,
}
impl<Float: Debug> $t<Float> {
pub fn new(a: Float, b: Float, epsilon: Float) -> Self {
$t {
a,
b,
epsilon,
}
}
}
impl<Float: Debug> Display for $t<Float> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, $msg, self.a, self.b, self.epsilon)
}
}
};
}
threshold_error_impl!(AbsoluteEqError, afe_absolute_eq_error_msg!());
threshold_error_impl!(AbsoluteNeError, afe_absolute_ne_error_msg!());
threshold_error_impl!(RelativeEqError, afe_relative_eq_error_msg!());
threshold_error_impl!(RelativeNeError, afe_relative_ne_error_msg!());
#[derive(Debug)]
#[doc(hidden)]
pub struct FloatNearError<Float: Debug, Int: Debug> {
a: Float,
b: Float,
n: Int,
previous: Float,
next: Float,
}
impl<Float: Debug, Int: Debug> FloatNearError<Float, Int> {
pub fn new(a: Float, b: Float, n: Int, previous: Float, next: Float) -> Self {
Self {
a,
b,
n,
previous,
next,
}
}
}
impl<Float: Debug, Int: Debug> Display for FloatNearError<Float, Int> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, afe_near_error_msg!(), self.a, self.b, self.n, self.previous, self.next)
}
}
#[derive(Debug)]
#[doc(hidden)]
pub struct FloatFarError<Float: Debug, Int: Debug> {
a: Float,
b: Float,
n: Int,
previous: Float,
next: Float,
}
impl<Float: Debug, Int: Debug> FloatFarError<Float, Int> {
pub fn new(a: Float, b: Float, n: Int, previous: Float, next: Float) -> Self {
Self {
a,
b,
n,
previous,
next,
}
}
}
impl<Float: Debug, Int: Debug> Display for FloatFarError<Float, Int> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, afe_far_error_msg!(), self.a, self.b, self.n, self.previous, self.next)
}
}
#[doc(hidden)]
pub fn bool_to_result<T: Display>(r: bool, err: T) -> Result<(), T> {
if r {
Ok(())
} else {
Err(err)
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_is_absolute_eq {
($a:ident, $b:ident, $epsilon:ident) => {{
let [a, b, epsilon] = [$a, $b, $epsilon].map(f64::from);
(a - b).abs() <= epsilon
}};
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_is_relative_eq {
($a:ident, $b:ident, $epsilon:ident) => {
if $a == 0.0 {
$b == 0.0
} else {
let [a, b, epsilon] = [$a, $b, $epsilon].map(f64::from);
if a.is_nan() {
false
} else if a.is_infinite() {
true
} else {
((a - b).abs() / a.abs()) <= epsilon
}
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_is_f32_near {
($a:ident, $b:ident, $n:ident) => {{
let previous = $crate::previous_n_f32($a, $n);
let next = $crate::next_n_f32($a, $n);
let r = $b >= previous && $b <= next;
(r, previous, next)
}};
}
#[macro_export]
#[doc(hidden)]
macro_rules! afe_is_f64_near {
($a:ident, $b:ident, $n:ident) => {{
let previous = $crate::previous_n_f64($a, $n);
let next = $crate::next_n_f64($a, $n);
let r = $b >= previous && $b <= next;
(r, previous, next)
}};
}
#[macro_export]
macro_rules! expect_float_absolute_eq {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_absolute_eq!(a, b, eps);
let e = $crate::AbsoluteEqError::new(a, b, eps);
$crate::bool_to_result(r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_float_absolute_eq!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! expect_float_absolute_ne {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_absolute_eq!(a, b, eps);
let e = $crate::AbsoluteNeError::new(a, b, eps);
$crate::bool_to_result(!r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_float_absolute_ne!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! expect_float_relative_eq {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_relative_eq!(a, b, eps);
let e = $crate::RelativeEqError::new(a, b, eps);
$crate::bool_to_result(r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_float_relative_eq!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! expect_float_relative_ne {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_relative_eq!(a, b, eps);
let e = $crate::RelativeNeError::new(a, b, eps);
$crate::bool_to_result(!r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_float_relative_ne!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! expect_f32_near {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f32_near!(a, b, n);
let e = $crate::FloatNearError::new(a, b, n, previous, next);
$crate::bool_to_result(r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_f32_near!($a, $b, 4)
};
}
#[macro_export]
macro_rules! expect_f32_far {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f32_near!(a, b, n);
let e = $crate::FloatFarError::new(a, b, n, previous, next);
$crate::bool_to_result(!r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_f32_far!($a, $b, 4)
};
}
#[macro_export]
macro_rules! expect_f64_near {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f64_near!(a, b, n);
let e = $crate::FloatNearError::new(a, b, n, previous, next);
$crate::bool_to_result(r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_f64_near!($a, $b, 4)
};
}
#[macro_export]
macro_rules! expect_f64_far {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f64_near!(a, b, n);
let e = $crate::FloatFarError::new(a, b, n, previous, next);
$crate::bool_to_result(!r, e)
}};
($a:expr, $b:expr) => {
$crate::expect_f64_far!($a, $b, 4)
};
}
#[macro_export]
macro_rules! assert_float_absolute_eq {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_absolute_eq!(a, b, eps);
assert!(r, $crate::afe_absolute_eq_error_msg!(), a, b, eps)
}};
($a:expr, $b:expr) => {
$crate::assert_float_absolute_eq!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! assert_float_absolute_ne {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_absolute_eq!(a, b, eps);
assert!(!r, $crate::afe_absolute_ne_error_msg!(), a, b, eps)
}};
($a:expr, $b:expr) => {
$crate::assert_float_absolute_ne!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! assert_float_relative_eq {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_relative_eq!(a, b, eps);
assert!(r, $crate::afe_relative_eq_error_msg!(), a, b, eps)
}};
($a:expr, $b:expr) => {
$crate::assert_float_relative_eq!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! assert_float_relative_ne {
($a:expr, $b:expr, $epsilon:expr) => {{
let (a, b, eps) = ($a, $b, $epsilon);
let r = $crate::afe_is_relative_eq!(a, b, eps);
assert!(!r, $crate::afe_relative_ne_error_msg!(), a, b, eps)
}};
($a:expr, $b:expr) => {
$crate::assert_float_relative_ne!($a, $b, 1.0e-6)
};
}
#[macro_export]
macro_rules! assert_f32_near {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f32_near!(a, b, n);
assert!(r, $crate::afe_near_error_msg!(), a, b, n, previous, next)
}};
($a:expr, $b:expr) => {
$crate::assert_f32_near!($a, $b, 4)
};
}
#[macro_export]
macro_rules! assert_f32_far {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f32_near!(a, b, n);
assert!(!r, $crate::afe_far_error_msg!(), a, b, n, previous, next)
}};
($a:expr, $b:expr) => {
$crate::assert_f32_far!($a, $b, 4)
};
}
#[macro_export]
macro_rules! assert_f64_near {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f64_near!(a, b, n);
assert!(r, $crate::afe_near_error_msg!(), a, b, n, previous, next)
}};
($a:expr, $b:expr) => {
$crate::assert_f64_near!($a, $b, 4)
};
}
#[macro_export]
macro_rules! assert_f64_far {
($a:expr, $b:expr, $n:expr) => {{
let (a, b, n) = ($a, $b, $n);
let (r, previous, next) = $crate::afe_is_f64_near!(a, b, n);
assert!(!r, $crate::afe_far_error_msg!(), a, b, n, previous, next)
}};
($a:expr, $b:expr) => {
$crate::assert_f64_far!($a, $b, 4)
};
}
#[cfg(test)]
mod tests {
use core::f32;
use super::*;
#[test]
#[should_panic]
fn absolute_eq_fail() {
assert_float_absolute_eq!(3.0, 4.0, 0.9);
}
#[test]
fn absolute_eq_succeed() {
assert_float_absolute_eq!(3.0, 4.0, 1.0);
}
#[test]
#[should_panic]
fn relative_eq_fail() {
assert_float_relative_eq!(4.0, 3.0, 0.2);
}
#[test]
fn relative_eq_succeed() {
assert_float_relative_eq!(4.0, 3.0, 0.26);
}
#[test]
#[should_panic]
fn relative_eq_negative_zero_fail() {
assert_float_relative_eq!(-0.1, 0.0);
}
#[test]
#[should_panic]
fn f32_near_fail() {
assert_f32_near!(1.0e-45, 7.0e-45, 3);
}
#[test]
fn f32_near_succeed() {
assert_f32_near!(1.0e-45, 7.0e-45, 4);
}
#[test]
#[should_panic]
fn f64_near_fail() {
assert_f64_near!(5.0e-324, 2.5e-323, 3);
}
#[test]
#[should_panic]
fn f64_far_fail() {
assert_f64_far!(5.0e-324, 2.5e-323, 4);
}
#[test]
fn f64_near_succeed() {
assert_f64_near!(5.0e-324, 2.5e-323, 4);
}
#[test]
fn f64_far_succeed() {
assert_f64_far!(5.0e-324, 3e-323, 4);
}
#[test]
fn issue_03() {
assert!(expect_float_relative_eq!(f32::INFINITY, f32::MAX, f32::INFINITY).is_ok());
assert!(expect_float_relative_eq!(f32::INFINITY, f32::MAX, 0.0).is_ok());
assert!(expect_float_relative_eq!(f32::MAX, f32::INFINITY, f32::INFINITY).is_ok());
let error = expect_float_relative_eq!(f32::MAX, f32::INFINITY, 1.0).unwrap_err();
assert_eq!(error.a, f32::MAX);
assert_eq!(error.b, f32::INFINITY);
assert_eq!(error.epsilon, 1.0);
}
}