c0nst::c0nst! {
pub c0nst trait FloatBits: Sized {
type Bits;
fn to_bits(self) -> Self::Bits;
fn from_bits(bits: Self::Bits) -> Self;
}
}
c0nst::c0nst! {
pub c0nst trait NextUp: Sized {
type Output;
fn next_up(self) -> Self::Output;
}
}
c0nst::c0nst! {
pub c0nst trait NextDown: Sized {
type Output;
fn next_down(self) -> Self::Output;
}
}
c0nst::c0nst! {
pub c0nst trait Maximum: Sized {
type Output;
fn maximum(self, other: Self) -> Self::Output;
}
}
c0nst::c0nst! {
pub c0nst trait Minimum: Sized {
type Output;
fn minimum(self, other: Self) -> Self::Output;
}
}
pub trait RoundTiesEven: Sized {
type Output;
fn round_ties_even(self) -> Self::Output;
}
c0nst::c0nst! {
pub c0nst trait Algebraic: Sized {
type Output;
fn algebraic_add(self, rhs: Self) -> Self::Output;
fn algebraic_sub(self, rhs: Self) -> Self::Output;
fn algebraic_mul(self, rhs: Self) -> Self::Output;
fn algebraic_div(self, rhs: Self) -> Self::Output;
fn algebraic_rem(self, rhs: Self) -> Self::Output;
}
}
pub trait Erf: Sized {
type Output;
fn erf(self) -> Self::Output;
fn erfc(self) -> Self::Output;
}
pub trait Gamma: Sized {
type Output;
fn gamma(self) -> Self::Output;
fn ln_gamma(self) -> (Self::Output, i32);
}
macro_rules! float_bits_impl {
($($t:ty => $b:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl FloatBits for $t {
type Bits = $b;
#[inline]
fn to_bits(self) -> $b {
<$t>::to_bits(self)
}
#[inline]
fn from_bits(bits: $b) -> $t {
<$t>::from_bits(bits)
}
}
}
c0nst::c0nst! {
c0nst impl NextUp for $t {
type Output = $t;
#[inline]
fn next_up(self) -> $t {
<$t>::next_up(self)
}
}
}
c0nst::c0nst! {
c0nst impl NextDown for $t {
type Output = $t;
#[inline]
fn next_down(self) -> $t {
<$t>::next_down(self)
}
}
}
c0nst::c0nst! {
c0nst impl Maximum for $t {
type Output = $t;
#[inline]
fn maximum(self, other: Self) -> $t {
if self > other {
self
} else if other > self {
other
} else if self == other {
if <$t>::is_sign_positive(self) && <$t>::is_sign_negative(other) {
self
} else {
other
}
} else {
self + other
}
}
}
}
c0nst::c0nst! {
c0nst impl Minimum for $t {
type Output = $t;
#[inline]
fn minimum(self, other: Self) -> $t {
if self < other {
self
} else if other < self {
other
} else if self == other {
if <$t>::is_sign_negative(self) && <$t>::is_sign_positive(other) {
self
} else {
other
}
} else {
self + other
}
}
}
}
c0nst::c0nst! {
c0nst impl Algebraic for $t {
type Output = $t;
#[inline]
fn algebraic_add(self, rhs: Self) -> $t { self + rhs }
#[inline]
fn algebraic_sub(self, rhs: Self) -> $t { self - rhs }
#[inline]
fn algebraic_mul(self, rhs: Self) -> $t { self * rhs }
#[inline]
fn algebraic_div(self, rhs: Self) -> $t { self / rhs }
#[inline]
fn algebraic_rem(self, rhs: Self) -> $t { self % rhs }
}
}
)*};
}
float_bits_impl! {
f32 => u32;
f64 => u64;
}
macro_rules! round_ties_even_impl {
($($t:ty)*) => {$(
#[cfg(feature = "std")]
impl RoundTiesEven for $t {
type Output = $t;
#[inline]
fn round_ties_even(self) -> $t {
<$t>::round_ties_even(self)
}
}
#[cfg(not(feature = "std"))]
impl RoundTiesEven for $t {
type Output = $t;
#[inline]
fn round_ties_even(self) -> $t {
use crate::float::FloatCore;
let f = FloatCore::fract(self);
if FloatCore::abs(f) != 0.5 {
FloatCore::round(self)
} else {
let t = FloatCore::trunc(self);
if t % 2.0 == 0.0 {
t
} else {
t + FloatCore::signum(self)
}
}
}
}
)*};
}
round_ties_even_impl!(f32 f64);
#[cfg(feature = "libm")]
impl Erf for f32 {
type Output = f32;
#[inline]
fn erf(self) -> f32 {
libm::erff(self)
}
#[inline]
fn erfc(self) -> f32 {
libm::erfcf(self)
}
}
#[cfg(feature = "libm")]
impl Erf for f64 {
type Output = f64;
#[inline]
fn erf(self) -> f64 {
libm::erf(self)
}
#[inline]
fn erfc(self) -> f64 {
libm::erfc(self)
}
}
#[cfg(feature = "libm")]
impl Gamma for f32 {
type Output = f32;
#[inline]
fn gamma(self) -> f32 {
libm::tgammaf(self)
}
#[inline]
fn ln_gamma(self) -> (f32, i32) {
libm::lgammaf_r(self)
}
}
#[cfg(feature = "libm")]
impl Gamma for f64 {
type Output = f64;
#[inline]
fn gamma(self) -> f64 {
libm::tgamma(self)
}
#[inline]
fn ln_gamma(self) -> (f64, i32) {
libm::lgamma_r(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bits_and_ulps() {
assert_eq!(FloatBits::to_bits(1.0f32), 0x3F80_0000u32);
let x: f64 = FloatBits::from_bits(0x4029_0000_0000_0000u64);
assert_eq!(x, 12.5);
assert_eq!(NextUp::next_up(1.0f32), f32::from_bits(0x3F80_0001));
assert_eq!(NextDown::next_down(1.0f32), f32::from_bits(0x3F7F_FFFF));
assert_eq!(NextUp::next_up(0.0f32), f32::from_bits(1)); }
#[test]
fn minimum_maximum() {
assert_eq!(Maximum::maximum(1.0f32, 2.0), 2.0);
assert_eq!(Minimum::minimum(1.0f32, 2.0), 1.0);
assert!(Maximum::maximum(1.0f32, f32::NAN).is_nan());
assert!(Minimum::minimum(f32::NAN, 1.0f32).is_nan());
assert_eq!(Maximum::maximum(0.0f32, -0.0).to_bits(), 0.0f32.to_bits());
assert_eq!(
Minimum::minimum(0.0f32, -0.0).to_bits(),
(-0.0f32).to_bits()
);
}
#[test]
fn ties_even() {
assert_eq!(RoundTiesEven::round_ties_even(2.5f32), 2.0);
assert_eq!(RoundTiesEven::round_ties_even(3.5f32), 4.0);
assert_eq!(RoundTiesEven::round_ties_even(-2.5f64), -2.0);
assert_eq!(RoundTiesEven::round_ties_even(-3.5f64), -4.0);
assert_eq!(RoundTiesEven::round_ties_even(2.4f32), 2.0);
assert_eq!(RoundTiesEven::round_ties_even(2.6f32), 3.0);
assert_eq!(RoundTiesEven::round_ties_even(0.5f64), 0.0);
assert_eq!(RoundTiesEven::round_ties_even(-0.5f64), -0.0);
}
#[test]
fn algebraic_is_conforming() {
assert_eq!(Algebraic::algebraic_add(1.5f64, 2.25), 3.75);
assert_eq!(Algebraic::algebraic_mul(3.0f32, 0.5), 1.5);
assert_eq!(Algebraic::algebraic_rem(7.5f64, 2.0), 1.5);
}
#[cfg(feature = "libm")]
#[test]
fn special_functions() {
assert!((Erf::erf(0.0f64)).abs() < 1e-15);
assert!((Erf::erf(10.0f64) - 1.0).abs() < 1e-15);
assert!((Erf::erfc(0.0f64) - 1.0).abs() < 1e-15);
assert!((Gamma::gamma(5.0f64) - 24.0).abs() < 1e-10);
let (lg, sign) = Gamma::ln_gamma(5.0f64);
assert!((lg - 24.0f64.ln()).abs() < 1e-10);
assert_eq!(sign, 1);
}
}