use crate::IntoInner;
#[derive(Debug, Clone, Copy)]
pub struct InfiniteError;
impl std::fmt::Display for InfiniteError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "encountered infinity or NaN unexpectedly")
}
}
#[allow(clippy::module_name_repetitions)]
pub trait IsFinite: Sized + Copy {
fn is_finite(self) -> bool;
}
#[derive(Debug, Clone, Copy, Default)]
#[repr(transparent)]
pub struct Finite<F: IsFinite>(F);
impl<F: IsFinite> Finite<F> {
pub fn try_new(val: F) -> Result<Self, InfiniteError> {
if val.is_finite() {
Ok(Self(val))
} else {
Err(InfiniteError)
}
}
pub const unsafe fn unchecked(val: F) -> Self {
union Transmute<F: IsFinite> {
val: F,
finite: Finite<F>,
}
Transmute { val }.finite
}
#[inline]
pub const fn val(self) -> F {
self.0
}
}
ctor_impls!(Finite<F: IsFinite>, "If the number is non-finite.");
impl<F: IsFinite> IntoInner<F> for Finite<F> {
#[inline]
fn into_inner(self) -> F {
self.val()
}
}
eq_impls!(Finite<F: IsFinite>);
ord_impls!(Finite<F: IsFinite>);
round_impls!(Finite<F: IsFinite>);
signed_impls!(Finite<F: IsFinite>);
sum_impls!(
Finite<F: IsFinite>,
InfiniteError,
"If the result is non-finite."
);
neg_impls!(
Finite<F: IsFinite>,
InfiniteError,
"If the result is non-finite."
);
product_impls!(
Finite<F: IsFinite>,
InfiniteError,
"If the result is non-finite."
);
impl<F: IsFinite + crate::ops::Pow> Finite<F> {
pow_methods!(F, InfiniteError, "If the result is non-finite.");
recip_methods!(F, InfiniteError, "If the result is non-finite.");
sqrt_methods!(F, InfiniteError, "If the result is non-finite.");
cbrt_methods!(F);
hypot_methods!(F, InfiniteError, "If the result is non-finite.");
}
exp_impls!(
Finite<F: IsFinite>,
InfiniteError,
"If the result is non-finite."
);
impl<F: IsFinite + crate::ops::Trig> Finite<F> {
sin_cos_methods!(F); tan_methods!(F, InfiniteError, "If the result is non-finite.");
asin_acos_methods!(
F,
InfiniteError,
"If the result is non-finite (caused if the magnitude of the input exceeds 1)."
);
atan_methods!(F); atan2_methods!(F, InfiniteError, "If the result is non-finite.");
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! finite {
($f: expr) => {
Finite::new($f)
};
}
#[test]
#[should_panic]
fn assert_new_nan() {
finite!(f32::NAN);
}
#[test]
#[should_panic]
fn assert_new_nan2() {
finite!(-f32::NAN);
}
#[test]
#[should_panic]
fn assert_new_inf() {
finite!(f32::INFINITY);
}
#[test]
#[should_panic]
fn assert_new_inf2() {
finite!(f32::NEG_INFINITY);
}
#[test]
fn unchecked() {
let finite = unsafe { Finite::unchecked(f32::INFINITY) };
assert!(finite.val().is_infinite());
}
#[test]
fn assert_nan() {
assert_err!(finite!(f32::MAX).try_add(f32::MAX));
assert_err!(finite!(f32::MIN).try_sub(f32::MAX));
assert_err!(finite!(f32::MAX).try_mul(f32::MAX));
assert_err!(finite!(1.0f32).try_div(0.0));
assert_err!(finite!(1.0f32).try_div(-0.0));
assert_err!(finite!(1.0f32).try_rem(0.0));
assert_err!(finite!(1000.0f32).try_powf(1000.0));
assert_err!(finite!(-1.0f32).try_sqrt());
assert_err!(finite!(-1.0f32).try_log(3.0));
assert_err!(finite!(-1.0f32).try_ln());
assert_err!(finite!(-1.0f32).try_log2());
assert_err!(finite!(-1.0f32).try_log10());
}
#[test]
fn assert_ops() {
assert_eq!(finite!(2.0f32) + 1.0, finite!(3.0));
assert_eq!(finite!(2.0f32) - 1.0, finite!(1.0));
assert_eq!(finite!(5.0f32) * 2.0, finite!(10.0));
assert_eq!(finite!(8.0f32) / 2.0, finite!(4.0));
assert_eq!(-finite!(1.0f32), finite!(-1.0));
}
#[test]
#[allow(clippy::bool_assert_comparison)]
#[allow(clippy::cmp_nan)]
fn assert_cmp_weird() {
assert!(finite!(-1.0f32) < finite!(0.0));
assert_eq!(finite!(0.0f32), finite!(0.0));
assert_eq!(finite!(0.0f32), finite!(-0.0));
assert_eq!(finite!(-0.0f32), finite!(0.0));
assert_eq!(finite!(-0.0f32), finite!(-0.0));
assert!(finite!(0.0) < finite!(1.0));
assert_eq!(finite!(1.0) < f32::NAN, false);
assert_eq!(finite!(1.0) >= f32::NAN, false);
}
#[test]
fn assert_pow() {
assert_eq!(finite!(4.0f32).powf(3.5), finite!(128.0));
assert_eq!(finite!(2.0f32).powi(8), finite!(256.0));
assert_eq!(finite!(2.0f32).recip(), finite!(0.5));
assert_eq!(finite!(4.0f32).sqrt(), finite!(2.0));
assert_eq!(finite!(27.0f32).cbrt(), finite!(3.0));
}
#[test]
fn assert_exp() {
assert_epsilon!(finite!(2.0f32).exp(), finite!(7.389_056));
assert_epsilon!(finite!(3.0f32).exp2(), finite!(8.0));
assert_epsilon!(finite!(5.0f32).exp_m1(), finite!(147.413_16));
assert_epsilon!(finite!(16.0f32).log(4.0), finite!(2.0));
assert_epsilon!(finite!(1.0f32).ln(), finite!(0.0));
assert_epsilon!(finite!(8.0f32).log2(), finite!(3.0));
assert_epsilon!(finite!(1000.0f32).log10(), finite!(3.0));
assert_epsilon!(finite!(147.413_16f32).ln_1p(), finite!(5.0));
}
#[test]
fn assert_trig() {
use std::f32::consts::{FRAC_1_SQRT_2, PI};
assert_epsilon!(finite!(0.0f32).sin(), finite!(0.0));
assert_epsilon!(finite!(PI / 4.0).sin(), finite!(FRAC_1_SQRT_2));
assert_epsilon!(finite!(PI / 2.0).sin(), finite!(1.0));
assert_epsilon!(finite!(0.0f32).cos(), finite!(1.0));
assert_epsilon!(finite!(PI / 4.0).cos(), finite!(FRAC_1_SQRT_2));
assert_epsilon!(finite!(PI / 2.0).cos(), 0.0);
assert_epsilon!(finite!(0.0f32).tan(), finite!(0.0));
assert_epsilon!(finite!(PI / 4.0).tan(), finite!(1.0));
assert_epsilon!(finite!(0.0f32).asin(), finite!(0.0));
assert_epsilon!(finite!(FRAC_1_SQRT_2).asin(), finite!(PI / 4.0));
assert_epsilon!(finite!(1.0f32).asin(), finite!(PI / 2.0));
assert_epsilon!(finite!(0.0f32).acos(), finite!(PI / 2.0));
assert_epsilon!(finite!(FRAC_1_SQRT_2).acos(), finite!(PI / 4.0));
assert_epsilon!(finite!(1.0f32).acos(), finite!(0.0));
assert_epsilon!(finite!(0.0f32).atan(), finite!(0.0));
assert_epsilon!(finite!(1.0f32).atan(), finite!(PI / 4.0));
}
}