#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
use core::{
cmp, fmt,
iter::{Product, Sum},
num::FpCategory,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign},
};
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())* ))
}
)*
};
($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())* ))
}
)*
};
}
mod math;
mod nalgebra;
mod num_traits;
mod poison;
use poison::MaybePoison;
#[derive(Clone, Debug, PartialEq)]
pub struct InvalidValueError {
_priv: (),
}
impl fmt::Display for InvalidValueError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("value may not be infinite or NaN")
}
}
impl std::error::Error for InvalidValueError {}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct FF32(MaybePoison<f32>);
#[inline(always)]
pub fn ff32(f: f32) -> FF32 {
FF32::new(f)
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct FF64(MaybePoison<f64>);
#[inline(always)]
pub fn ff64(f: f64) -> FF64 {
FF64::new(f)
}
macro_rules! impl_assign_ops {
($fast_ty:ident, $base_ty: ident: $($op_trait:ident, $op_fn:ident, $op:ident,)*) => {
$(
impl $op_trait <$fast_ty> for $fast_ty {
#[inline(always)]
fn $op_fn(&mut self, rhs: $fast_ty) {
*self = <$fast_ty>::$op(*self, rhs)
}
}
impl $op_trait <&$fast_ty> for $fast_ty {
#[inline(always)]
fn $op_fn(&mut self, rhs: &$fast_ty) {
*self = <$fast_ty>::$op(*self, rhs)
}
}
impl $op_trait <$base_ty> for $fast_ty {
#[inline(always)]
fn $op_fn(&mut self, rhs: $base_ty) {
*self = <$fast_ty>::$op(*self, rhs)
}
}
impl $op_trait <&$base_ty> for $fast_ty {
#[inline(always)]
fn $op_fn(&mut self, rhs: &$base_ty) {
*self = <$fast_ty>::$op(*self, rhs)
}
}
)*
}
}
macro_rules! impl_reduce_ops {
($fast_ty:ident, $base_ty: ident: $($op_trait:ident, $op_fn:ident, $op:ident, $identity:expr,)*) => {
$(
impl $op_trait <$fast_ty> for $fast_ty {
#[inline]
fn $op_fn <I> (iter: I) -> Self
where I: Iterator<Item = $fast_ty>
{
iter.fold($identity, |acc, val| acc.$op(val))
}
}
impl<'a> $op_trait <&'a $fast_ty> for $fast_ty {
#[inline]
fn $op_fn <I> (iter: I) -> Self
where I: Iterator<Item = &'a $fast_ty>
{
iter.fold($identity, |acc, val| acc.$op(val))
}
}
impl $op_trait <$base_ty> for $fast_ty {
#[inline]
fn $op_fn <I> (iter: I) -> Self
where I: Iterator<Item = $base_ty>
{
iter.fold($identity, |acc, val| acc.$op(val))
}
}
impl<'a> $op_trait <&'a $base_ty> for $fast_ty {
#[inline]
fn $op_fn <I> (iter: I) -> Self
where I: Iterator<Item = &'a $base_ty>
{
iter.fold($identity, |acc, val| acc.$op(val))
}
}
)*
}
}
macro_rules! impl_fmt {
($fast_ty:ident, $base_ty:ident, $($fmt_trait:path,)*) => {
$(
impl $fmt_trait for $fast_ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<$base_ty as $fmt_trait>::fmt(&self.freeze_raw(), f)
}
}
)*
}
}
macro_rules! impls {
($fast_ty:ident, $base_ty: ident) => {
impl $fast_ty {
const ONE: $fast_ty = <$fast_ty>::new(1.0);
const ZERO: $fast_ty = <$fast_ty>::new(0.0);
pub const MIN: $fast_ty = <$fast_ty>::new($base_ty::MIN);
pub const MIN_POSITIVE: $fast_ty = <$fast_ty>::new($base_ty::MIN_POSITIVE);
pub const MAX: $fast_ty = <$fast_ty>::new($base_ty::MAX);
#[doc = "Create a new `"]
#[doc = stringify!($fast_ty)]
#[doc = "` instance from the given float value."]
#[inline(always)]
pub const fn new(f: $base_ty) -> Self {
$fast_ty(MaybePoison::new(f))
}
#[doc = "Create a new `"]
#[doc = stringify!($fast_ty)]
#[doc = "` instance from the given float value, returning an error if the value is infinite or NaN."]
#[inline(always)]
pub fn new_checked(f: $base_ty) -> Result<Self, InvalidValueError> {
if f.is_finite() {
Ok($fast_ty::new(f))
} else {
Err(InvalidValueError { _priv: () })
}
}
#[inline(always)]
fn freeze_raw(self) -> $base_ty {
self.0.freeze()
}
forward_freeze_self! {
$fast_ty, $base_ty
pub fn div_euclid(self, rhs: Self) -> Self;
pub fn rem_euclid(self, rhs: Self) -> Self;
pub fn to_degrees(self) -> Self;
pub fn to_radians(self) -> Self;
}
#[inline]
pub fn classify(self) -> FpCategory {
match self.freeze_raw().classify() {
FpCategory::Nan | FpCategory::Infinite => FpCategory::Normal,
category => category
}
}
#[inline]
pub fn is_sign_negative(self) -> bool {
self.freeze_raw().is_sign_negative()
}
#[inline]
pub fn is_sign_positive(self) -> bool {
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
}
#[inline]
pub fn hypot(self, other: Self) -> Self {
(self * self + other * other).sqrt()
}
#[inline]
pub fn signum(self) -> Self {
Self::ONE.copysign(self)
}
#[inline]
pub fn recip(self) -> Self {
Self::ONE / self
}
#[inline]
pub fn fract(self) -> Self {
self - self.trunc()
}
#[inline]
pub fn log(self, base: Self) -> Self {
self.ln() / base.ln()
}
#[inline]
pub fn mul_add(self, mul: Self, add: Self) -> Self {
self * mul + add
}
#[inline]
pub fn sin_cos(self) -> (Self, Self) {
(self.sin(), self.cos())
}
}
impl_fmt! {
$fast_ty, $base_ty,
fmt::Debug, fmt::Display, fmt::LowerExp, fmt::UpperExp,
}
impl_assign_ops! {
$fast_ty, $base_ty:
AddAssign, add_assign, add,
SubAssign, sub_assign, sub,
MulAssign, mul_assign, mul,
DivAssign, div_assign, div,
RemAssign, rem_assign, rem,
}
impl_reduce_ops! {
$fast_ty, $base_ty:
Sum, sum, add, Self::ZERO,
Product, product, mul, Self::ONE,
}
impl PartialEq<$fast_ty> for $fast_ty {
#[inline]
fn eq(&self, other: &$fast_ty) -> bool {
let this = self.freeze_raw();
let that = other.freeze_raw();
this == that
}
}
impl PartialEq<$base_ty> for $fast_ty {
#[inline]
fn eq(&self, other: &$base_ty) -> bool {
let this = self.freeze_raw();
let that = *other;
this == that
}
}
impl PartialEq<$fast_ty> for $base_ty {
#[inline]
fn eq(&self, other: &$fast_ty) -> bool {
let this = *self;
let that = other.freeze_raw();
this == that
}
}
impl PartialOrd<$fast_ty> for $fast_ty {
#[inline(always)]
fn partial_cmp(&self, other: &$fast_ty) -> Option<cmp::Ordering> {
<$base_ty>::partial_cmp(&self.freeze_raw(), &other.freeze_raw())
}
#[inline(always)]
fn lt(&self, other: &$fast_ty) -> bool {
self.freeze_raw() < other.freeze_raw()
}
#[inline(always)]
fn le(&self, other: &$fast_ty) -> bool {
self.freeze_raw() <= other.freeze_raw()
}
#[inline(always)]
fn gt(&self, other: &$fast_ty) -> bool {
self.freeze_raw() > other.freeze_raw()
}
#[inline(always)]
fn ge(&self, other: &$fast_ty) -> bool {
self.freeze_raw() >= other.freeze_raw()
}
}
impl From<$fast_ty> for $base_ty {
#[inline(always)]
fn from(from: $fast_ty) -> Self {
from.freeze_raw()
}
}
impl From<$base_ty> for $fast_ty {
#[inline(always)]
fn from(from: $base_ty) -> Self {
<$fast_ty>::new(from)
}
}
};
}
impls! { FF32, f32 }
impls! { FF64, f64 }