Naive math functions and num-trait impls
This commit is contained in:
375
src/lib.rs
375
src/lib.rs
@@ -5,6 +5,7 @@ use core::{
|
|||||||
cmp, fmt,
|
cmp, fmt,
|
||||||
intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast},
|
intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast},
|
||||||
iter::{Product, Sum},
|
iter::{Product, Sum},
|
||||||
|
num::FpCategory,
|
||||||
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
|
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -265,6 +266,259 @@ macro_rules! impl_fmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-traits")]
|
||||||
|
macro_rules! impl_num_traits {
|
||||||
|
($fast_ty:ident, $base_ty:ident) => {
|
||||||
|
impl num_traits::One for $fast_ty {
|
||||||
|
#[inline(always)]
|
||||||
|
fn one() -> Self {
|
||||||
|
Self::ONE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_one(&self) -> bool {
|
||||||
|
self.freeze_raw() == 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl num_traits::Zero for $fast_ty {
|
||||||
|
#[inline(always)]
|
||||||
|
fn zero() -> Self {
|
||||||
|
Self::ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.freeze_raw() == 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl num_traits::Num for $fast_ty {
|
||||||
|
type FromStrRadixErr = <$base_ty as num_traits::Num>::FromStrRadixErr;
|
||||||
|
|
||||||
|
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
|
||||||
|
Ok(<$fast_ty>::new(
|
||||||
|
<$base_ty as num_traits::Num>::from_str_radix(str, radix)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl num_traits::ToPrimitive for $fast_ty {
|
||||||
|
forward_freeze_ty! {
|
||||||
|
$fast_ty, $base_ty
|
||||||
|
fn to_isize(&self) -> Option<isize> ;
|
||||||
|
fn to_i8(&self) -> Option<i8> ;
|
||||||
|
fn to_i16(&self) -> Option<i16> ;
|
||||||
|
fn to_i32(&self) -> Option<i32> ;
|
||||||
|
fn to_i64(&self) -> Option<i64> ;
|
||||||
|
fn to_i128(&self) -> Option<i128> ;
|
||||||
|
|
||||||
|
fn to_usize(&self) -> Option<usize> ;
|
||||||
|
fn to_u8(&self) -> Option<u8> ;
|
||||||
|
fn to_u16(&self) -> Option<u16> ;
|
||||||
|
fn to_u32(&self) -> Option<u32> ;
|
||||||
|
fn to_u64(&self) -> Option<u64> ;
|
||||||
|
fn to_u128(&self) -> Option<u128> ;
|
||||||
|
|
||||||
|
fn to_f32(&self) -> Option<f32> ;
|
||||||
|
fn to_f64(&self) -> Option<f64> ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl num_traits::NumCast for $fast_ty {
|
||||||
|
#[inline]
|
||||||
|
fn from<N: num_traits::ToPrimitive>(n: N) -> Option<Self> {
|
||||||
|
Some(<$fast_ty>::new(<$base_ty as num_traits::NumCast>::from(n)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Because inf and nan are prohibited, the `fast_fp` types correspond more to the `Real`
|
||||||
|
/// trait than the `Float` trait. However in practice some libs require a Float bound when
|
||||||
|
/// they could really use a Real, which would restrict using the `fast_fp` types.
|
||||||
|
impl num_traits::Float for $fast_ty {
|
||||||
|
/// Panics because NaN values are not supported
|
||||||
|
#[inline]
|
||||||
|
fn nan() -> Self {
|
||||||
|
panic!(concat!(
|
||||||
|
stringify!($fast_ty),
|
||||||
|
" does not support NaN values"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panics because infinite values are not supported
|
||||||
|
///
|
||||||
|
/// Consider using [`max_value`](num_traits::Float::max_value) as appropriate instead
|
||||||
|
#[inline]
|
||||||
|
fn infinity() -> Self {
|
||||||
|
panic!(concat!(
|
||||||
|
stringify!($fast_ty),
|
||||||
|
" does not support infinite values. Consider using `max_value` for comparisons"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panics because infinite values are not supported
|
||||||
|
///
|
||||||
|
/// Consider using [`min_value`](num_traits::Float::min_value) as appropriate instead
|
||||||
|
#[inline]
|
||||||
|
fn neg_infinity() -> Self {
|
||||||
|
panic!(concat!(
|
||||||
|
stringify!($fast_ty),
|
||||||
|
" does not support infinite values. Consider using `min_value` for comparisons"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn neg_zero() -> Self {
|
||||||
|
-Self::ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min_value() -> Self {
|
||||||
|
$fast_ty::MIN
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min_positive_value() -> Self {
|
||||||
|
$fast_ty::MIN_POSITIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn max_value() -> Self {
|
||||||
|
$fast_ty::MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn epsilon() -> Self {
|
||||||
|
<$fast_ty>::new($base_ty::EPSILON)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_nan(self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_infinite(self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_finite(self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_self! {
|
||||||
|
$fast_ty, $base_ty
|
||||||
|
fn is_normal(self) -> bool;
|
||||||
|
fn classify(self) -> FpCategory;
|
||||||
|
fn floor(self) -> Self;
|
||||||
|
fn ceil(self) -> Self;
|
||||||
|
fn round(self) -> Self;
|
||||||
|
fn trunc(self) -> Self;
|
||||||
|
fn fract(self) -> Self;
|
||||||
|
fn abs(self) -> Self;
|
||||||
|
fn signum(self) -> Self;
|
||||||
|
fn is_sign_positive(self) -> bool;
|
||||||
|
fn is_sign_negative(self) -> bool;
|
||||||
|
fn mul_add(self, a: Self, b: Self) -> Self;
|
||||||
|
fn recip(self) -> Self;
|
||||||
|
fn powi(self, n: i32) -> Self;
|
||||||
|
fn powf(self, n: Self) -> Self;
|
||||||
|
fn sqrt(self) -> Self;
|
||||||
|
fn exp(self) -> Self;
|
||||||
|
fn exp2(self) -> Self;
|
||||||
|
fn ln(self) -> Self;
|
||||||
|
fn log(self, base: Self) -> Self;
|
||||||
|
fn log2(self) -> Self;
|
||||||
|
fn log10(self) -> Self;
|
||||||
|
fn max(self, other: Self) -> Self;
|
||||||
|
fn min(self, other: Self) -> Self;
|
||||||
|
fn cbrt(self) -> Self;
|
||||||
|
fn hypot(self, other: Self) -> Self;
|
||||||
|
fn sin(self) -> Self;
|
||||||
|
fn cos(self) -> Self;
|
||||||
|
fn tan(self) -> Self;
|
||||||
|
fn asin(self) -> Self;
|
||||||
|
fn acos(self) -> Self;
|
||||||
|
fn atan(self) -> Self;
|
||||||
|
fn atan2(self, other: Self) -> Self;
|
||||||
|
fn sin_cos(self) -> (Self, Self);
|
||||||
|
fn exp_m1(self) -> Self;
|
||||||
|
fn ln_1p(self) -> Self;
|
||||||
|
fn sinh(self) -> Self;
|
||||||
|
fn cosh(self) -> Self;
|
||||||
|
fn tanh(self) -> Self;
|
||||||
|
fn asinh(self) -> Self;
|
||||||
|
fn acosh(self) -> Self;
|
||||||
|
fn atanh(self) -> Self;
|
||||||
|
fn to_degrees(self) -> Self;
|
||||||
|
fn to_radians(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_freeze_self! {
|
||||||
|
$fast_ty, $base_ty
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn abs_sub(self, other: Self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn integer_decode(self) -> (u64, i16, i8) {
|
||||||
|
<$base_ty as num_traits::Float>::integer_decode(self.freeze_raw())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! forward_freeze_self {
|
||||||
|
($fast_ty:ident, $base_ty:ident
|
||||||
|
$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$vis:vis fn $fn_name:ident (self $(, $arg:ident : Self)* ) -> Self ;
|
||||||
|
)*) => {
|
||||||
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
|
#[inline]
|
||||||
|
$vis fn $fn_name(self $(, $arg : Self)*) -> Self {
|
||||||
|
<$fast_ty>::new(<$base_ty>::$fn_name(self.freeze_raw() $(, $arg.freeze_raw())* ))
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-traits")]
|
||||||
|
macro_rules! forward_freeze_ty {
|
||||||
|
($fast_ty:ident, $base_ty:ident
|
||||||
|
$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$vis:vis fn $fn_name:ident (&self) -> $ret_ty:ty ;
|
||||||
|
)*) => {
|
||||||
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
|
#[inline]
|
||||||
|
$vis fn $fn_name(&self) -> $ret_ty {
|
||||||
|
<$base_ty>::$fn_name(&self.freeze_raw())
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-traits")]
|
||||||
|
macro_rules! forward_self {
|
||||||
|
($fast_ty:ident, $base_ty:ident
|
||||||
|
$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$vis:vis fn $fn_name:ident (self $(, $arg:ident : $arg_ty:ty)* ) -> $ret_ty:ty ;
|
||||||
|
)*) => {
|
||||||
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
|
#[inline]
|
||||||
|
$vis fn $fn_name(self $(, $arg : $arg_ty)*) -> $ret_ty {
|
||||||
|
<$fast_ty>::$fn_name(self $(, $arg)* )
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impls {
|
macro_rules! impls {
|
||||||
($fast_ty:ident, $base_ty: ident) => {
|
($fast_ty:ident, $base_ty: ident) => {
|
||||||
impl $fast_ty {
|
impl $fast_ty {
|
||||||
@@ -308,6 +562,106 @@ macro_rules! impls {
|
|||||||
// every bit pattern is valid in float
|
// every bit pattern is valid in float
|
||||||
unsafe { inner.assume_init() }
|
unsafe { inner.assume_init() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO migrate these to native implementations to freeze less and fast-math more
|
||||||
|
forward_freeze_self! {
|
||||||
|
$fast_ty, $base_ty
|
||||||
|
pub fn abs(self) -> Self;
|
||||||
|
pub fn acos(self) -> Self;
|
||||||
|
pub fn acosh(self) -> Self;
|
||||||
|
pub fn asin(self) -> Self;
|
||||||
|
pub fn asinh(self) -> Self;
|
||||||
|
pub fn atan(self) -> Self;
|
||||||
|
pub fn atan2(self, other: Self) -> Self;
|
||||||
|
pub fn atanh(self) -> Self;
|
||||||
|
pub fn cbrt(self) -> Self;
|
||||||
|
pub fn ceil(self) -> Self;
|
||||||
|
pub fn clamp(self, min: Self, max: Self) -> Self;
|
||||||
|
pub fn copysign(self, sign: Self) -> Self;
|
||||||
|
pub fn cos(self) -> Self;
|
||||||
|
pub fn cosh(self) -> Self;
|
||||||
|
pub fn div_euclid(self, rhs: Self) -> Self;
|
||||||
|
pub fn exp(self) -> Self;
|
||||||
|
pub fn exp2(self) -> Self;
|
||||||
|
pub fn exp_m1(self) -> Self;
|
||||||
|
pub fn floor(self) -> Self;
|
||||||
|
pub fn fract(self) -> Self;
|
||||||
|
pub fn hypot(self, other: Self) -> Self;
|
||||||
|
pub fn ln(self) -> Self;
|
||||||
|
pub fn ln_1p(self) -> Self;
|
||||||
|
pub fn log(self, base: Self) -> Self;
|
||||||
|
pub fn log10(self) -> Self;
|
||||||
|
pub fn log2(self) -> Self;
|
||||||
|
pub fn max(self, other: Self) -> Self;
|
||||||
|
pub fn min(self, other: Self) -> Self;
|
||||||
|
pub fn mul_add(self, a: Self, b: Self) -> Self;
|
||||||
|
pub fn powf(self, n: Self) -> Self;
|
||||||
|
pub fn recip(self) -> Self;
|
||||||
|
pub fn rem_euclid(self, rhs: Self) -> Self;
|
||||||
|
pub fn round(self) -> Self;
|
||||||
|
pub fn signum(self) -> Self;
|
||||||
|
pub fn sin(self) -> Self;
|
||||||
|
pub fn sinh(self) -> Self;
|
||||||
|
pub fn sqrt(self) -> Self;
|
||||||
|
pub fn tan(self) -> Self;
|
||||||
|
pub fn tanh(self) -> Self;
|
||||||
|
pub fn to_degrees(self) -> Self;
|
||||||
|
pub fn to_radians(self) -> Self;
|
||||||
|
pub fn trunc(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn powi(self, n: i32) -> Self {
|
||||||
|
<$fast_ty>::new(self.freeze_raw().powi(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn sin_cos(self) -> (Self, Self) {
|
||||||
|
let (sin, cos) = self.freeze_raw().sin_cos();
|
||||||
|
(<$fast_ty>::new(sin), <$fast_ty>::new(cos))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn classify(self) -> FpCategory {
|
||||||
|
// NaN and infinity should not be presented as possibilities to users, even if
|
||||||
|
// freeze ends up producing it. Results are unspecified, so Normal is just as valid
|
||||||
|
// as any other answer
|
||||||
|
match self.freeze_raw().classify() {
|
||||||
|
FpCategory::Nan | FpCategory::Infinite => FpCategory::Normal,
|
||||||
|
category => category
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_sign_negative(self) -> bool {
|
||||||
|
// must freeze to keep poison out of bool branching
|
||||||
|
self.freeze_raw().is_sign_negative()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_sign_positive(self) -> bool {
|
||||||
|
// must freeze to keep poison out of bool branching
|
||||||
|
self.freeze_raw().is_sign_positive()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_normal(self) -> bool {
|
||||||
|
self.classify() == FpCategory::Normal
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_subnormal(self) -> bool {
|
||||||
|
self.classify() == FpCategory::Subnormal
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The smallest finite value
|
||||||
|
pub const MIN: $fast_ty = <$fast_ty>::new($base_ty::MIN);
|
||||||
|
|
||||||
|
/// The smallest positive value
|
||||||
|
pub const MIN_POSITIVE: $fast_ty = <$fast_ty>::new($base_ty::MIN_POSITIVE);
|
||||||
|
|
||||||
|
/// The largest finite value
|
||||||
|
pub const MAX: $fast_ty = <$fast_ty>::new($base_ty::MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_fmt! {
|
impl_fmt! {
|
||||||
@@ -454,15 +808,19 @@ macro_rules! impls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min(self, other: $fast_ty) -> $fast_ty {
|
||||||
|
<$fast_ty>::min(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn max(self, other: $fast_ty) -> $fast_ty {
|
||||||
|
<$fast_ty>::max(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clamp(self, min: $fast_ty, max: $fast_ty) -> $fast_ty {
|
fn clamp(self, min: $fast_ty, max: $fast_ty) -> $fast_ty {
|
||||||
// TODO implement in terms of min/max,
|
<$fast_ty>::clamp(self, min, max)
|
||||||
// TODO also implement min/max (intrinsics? we don't want branches)
|
|
||||||
<$fast_ty>::new($base_ty::clamp(
|
|
||||||
self.freeze_raw(),
|
|
||||||
min.freeze_raw(),
|
|
||||||
max.freeze_raw(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,6 +839,9 @@ macro_rules! impls {
|
|||||||
<$fast_ty>::new(from)
|
<$fast_ty>::new(from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-traits")]
|
||||||
|
impl_num_traits! { $fast_ty, $base_ty }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user