use crate::{FFloat, FastFloat};
use core::ops::{
Add, AddAssign, Deref, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
};
#[cfg(doc)]
use std::f32::{INFINITY as INF, NAN};
macro_rules! simp {
($doc:literal trait $trat:ident with $($name:ident$(($arg:ident))?),+) => {
#[doc = $doc]
pub trait $trat {
$(
#[doc = concat!("Refer to [`f32::", stringify!($name), "`]")]
fn $name(self $(, $arg: Self)?) -> Self;
)+
}
impl $trat for f32 { $(fn $name(self $(, $arg: Self)?) -> Self { self.$name($($arg)?) })+ }
impl $trat for f64 { $(fn $name(self $(, $arg: Self)?) -> Self { self.$name($($arg)?) })+ }
impl<T: FastFloat + Trig + Rounding + Log> $trat for FFloat<T> {
$(
#[doc = include_str!("ffloat_safety_notice.md")]
fn $name(self $(, $arg: Self)?) -> Self { unsafe { FFloat::new(self.deref().$name($(*$arg)?)) } }
)+
}
};
}
simp!["Trigonometry functions" trait Trig with sin, asin, sinh, asinh, cos, acos, cosh, acosh, tan, atan, atan2(other), tanh, atanh];
simp!["Rounding functions" trait Rounding with floor, ceil, round];
simp!["Logarithm functions" trait Log with log(base), log2, log10, ln];
pub trait Constants {
#[doc = include_str!("refer.md")]
unsafe fn π() -> Self;
#[doc = include_str!("refer.md")]
unsafe fn ε() -> Self;
#[doc = include_str!("refer.md")]
unsafe fn e() -> Self;
}
macro_rules! ctor {
($for:ident) => {
impl Constants for $for {
unsafe fn π() -> $for {
std::$for::consts::PI
}
unsafe fn e() -> $for {
std::$for::consts::E
}
unsafe fn ε() -> $for {
$for::EPSILON
}
}
impl Constructors for $for {
unsafe fn zero() -> $for {
0.0
}
unsafe fn one() -> $for {
1.0
}
#[doc = concat!("Returns [`", stringify!($for), "::MIN`]. This function is safe to call")]
unsafe fn min() -> $for {
<$for>::MIN
}
#[doc = concat!("Returns [`", stringify!($for), "::MAX`]. This function is safe to call")]
unsafe fn max() -> $for {
<$for>::MAX
}
}
};
}
ctor!(f32);
ctor!(f64);
pub trait Constructors {
#[doc = include_str!("refer.md")]
unsafe fn zero() -> Self;
#[doc = include_str!("refer.md")]
unsafe fn one() -> Self;
#[doc = include_str!("refer.md")]
unsafe fn min() -> Self;
#[doc = include_str!("refer.md")]
unsafe fn max() -> Self;
}
pub trait FloatMethods: Trig + Rounding + Log {
fn trunc(self) -> Self;
fn fract(self) -> Self;
fn abs(self) -> Self;
fn powi(self, n: i32) -> Self;
fn powf(self, n: Self) -> Self;
fn sqrt(self) -> Self;
fn cbrt(self) -> Self;
fn hypot(self, other: Self) -> Self;
fn exp2(self) -> Self;
fn min(self, other: Self) -> Self;
fn max(self, other: Self) -> Self;
}
pub trait FloatAlone:
PartialEq
+ PartialOrd
+ Copy
+ Constructors
+ Constants
+ FloatMethods
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Rem<Self, Output = Self>
+ Div<Self, Output = Self>
+ Neg<Output = Self>
+ AddAssign<Self>
+ SubAssign<Self>
+ MulAssign<Self>
+ DivAssign<Self>
+ RemAssign<Self>
{
}
impl<
T: PartialEq
+ PartialOrd
+ Copy
+ Constructors
+ FloatMethods
+ Constants
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Mul<T, Output = T>
+ Rem<T, Output = T>
+ Div<T, Output = T>
+ Neg<Output = T>
+ AddAssign<T>
+ SubAssign<T>
+ MulAssign<T>
+ DivAssign<T>
+ RemAssign<T>,
> FloatAlone for T
{
}
pub trait Float<F>:
PartialOrd<F>
+ FloatAlone
+ Add<F, Output = Self>
+ Sub<F, Output = Self>
+ Mul<F, Output = Self>
+ Rem<F, Output = Self>
+ Div<F, Output = Self>
+ AddAssign<F>
+ SubAssign<F>
+ MulAssign<F>
+ DivAssign<F>
+ RemAssign<F>
where
Self: Sized,
{
#[doc = include_str!("refer.md")]
unsafe fn new(from: F) -> Self;
fn take(self) -> F;
}
macro_rules! impf {
($for:ty) => {
impl Float<$for> for $for {
unsafe fn new(from: $for) -> $for {
from
}
fn take(self) -> $for {
self
}
}
impl FloatMethods for $for {
fn trunc(self) -> $for {
self.trunc()
}
fn fract(self) -> $for {
self.fract()
}
fn abs(self) -> $for {
self.abs()
}
fn powi(self, n: i32) -> $for {
self.powi(n)
}
fn powf(self, n: $for) -> $for {
self.powf(n)
}
fn sqrt(self) -> $for {
self.sqrt()
}
fn cbrt(self) -> $for {
self.cbrt()
}
fn hypot(self, other: Self) -> $for {
self.hypot(other)
}
fn exp2(self) -> $for {
self.exp2()
}
fn min(self, other: Self) -> Self {
self.min(other)
}
fn max(self, other: Self) -> Self {
self.max(other)
}
}
};
}
impf!(f32);
impf!(f64);
impl<F: FastFloat + Constants> Constants for FFloat<F> {
#[doc = include_str!("ffloat_safety_noconstr.md")]
unsafe fn ε() -> Self {
Self::new(F::ε())
}
#[doc = include_str!("ffloat_safety_noconstr.md")]
unsafe fn π() -> Self {
Self::new(F::π())
}
#[doc = include_str!("ffloat_safety_noconstr.md")]
unsafe fn e() -> Self {
Self::new(F::e())
}
}
impl<F: FastFloat + Constructors> Constructors for FFloat<F> {
#[doc = include_str!("ffloat_safety_noconstr.md")]
unsafe fn zero() -> Self {
Self::new(F::zero())
}
#[doc = include_str!("ffloat_safety_noconstr.md")]
unsafe fn one() -> Self {
Self::new(F::one())
}
#[doc = include_str!("ffloat_safety_noconstr.md")]
unsafe fn min() -> Self {
Self::new(F::min())
}
#[doc = include_str!("ffloat_safety_noconstr.md")]
unsafe fn max() -> Self {
Self::new(F::max())
}
}
macro_rules! reuse {
(fn $name:ident) => {
#[doc = concat!("Refer to [`f32::", stringify!($name), "`]")]
#[doc = include_str!("ffloat_safety_notice.md")]
fn $name(self) -> Self {
self.check();
unsafe { Self::new(self.0.$name()) }
}
};
}
impl<F: FastFloat + Float<F>> Float<F> for FFloat<F> {
#[doc = include_str!("ffloat_safety.md")]
unsafe fn new(from: F) -> Self {
Self::new(from)
}
fn take(self) -> F {
self.0
}
}
impl<F: FloatMethods + FastFloat + Float<F>> FloatMethods for FFloat<F> {
reuse!(fn trunc);
reuse!(fn fract);
reuse!(fn abs);
#[doc = include_str!("ffloat_safety_notice.md")]
fn powi(self, n: i32) -> Self {
unsafe { Self::new(self.0.powi(n)) }
}
#[doc = include_str!("ffloat_safety_notice.md")]
fn powf(self, n: Self) -> Self {
self.check();
unsafe { Self::new(self.0.powf(*n)) }
}
reuse!(fn sqrt);
reuse!(fn cbrt);
#[doc = include_str!("ffloat_safety_notice.md")]
fn hypot(self, other: Self) -> Self {
self.check();
unsafe { Self::new(self.0.hypot(*other)) }
}
reuse!(fn exp2);
#[doc = include_str!("ffloat_safety_notice.md")]
fn min(self, other: Self) -> Self {
self.check();
unsafe { Self::new(self.0.min(*other)) }
}
#[doc = include_str!("ffloat_safety_notice.md")]
fn max(self, other: Self) -> Self {
self.check();
unsafe { Self::new(self.0.max(*other)) }
}
}
#[test]
fn usable() {
fn cos<F: Float<f32>>(x: F) -> F {
let mut y = x * (1.0 / 6.283);
y -= (y + 0.25).floor() + 0.25;
y *= (y.abs() - 0.5) * 16.0;
return y;
}
assert!((0.995..0.996).contains(&cos(0.1)));
assert!((0.995..0.996).contains(&*cos(unsafe { FFloat::new(0.1) })));
}