rust ffast-math (defunct, use lower)
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Cargo.toml | 9 | ||||
| -rw-r--r-- | LICENSE | 21 | ||||
| -rw-r--r-- | src/lib.rs | 144 | ||||
| -rw-r--r-- | src/trait.rs | 30 |
5 files changed, 206 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cd66a30 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "umath" +version = "0.0.0" +description = "ffast-math in rust" +license = "MIT" +repository = "https://github.com/bend-n/umath" +authors = ["bend-n <[email protected]>"] +edition = "2021" +exclude = [".gitignore"] @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 bendn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f7a3c46 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,144 @@ +//! library providing a fast float wrapper. +//! ``` +//! # use umath::FFloat; +//! # unsafe { +//! let mut f = FFloat::new(5.0); +//! f *= 7.0; +//! assert_eq!(*f, 35.0); +//! # } +//! ``` +#![feature(core_intrinsics)] +#![warn(clippy::pedantic, clippy::dbg_macro, clippy::use_self, missing_docs)] +use std::ops::{ + Add as add, AddAssign as add_assign, Deref, DerefMut, Div as div, DivAssign as div_assign, + Mul as mul, MulAssign as mul_assign, Rem as rem, RemAssign as rem_assign, Sub as sub, + SubAssign as sub_assign, +}; + +mod r#trait; +use r#trait::FastFloat; + +/// Float wrapper that uses `ffast-math`. +/// ``` +/// # use umath::FFloat; +/// # unsafe { +/// let result = FFloat::new(27.0) * 42109.0; +/// assert_eq!(*result, 1136943.0); +/// # } +/// ``` +pub struct FFloat<T>(T); + +impl<T: FastFloat> FFloat<T> { + /// Create a new [`FFloat`] from your {[`f32`], [`f64`]}. + /// There is no checked new, because it needs to be `unsafe` so that i can make sure you will never do any funny. + /// + /// # Safety + /// + /// you must solemnly swear that your number is not [`NAN`](std::f32::NAN) | [`INF`](std::f32::INFINITY), and you will not make it [`NAN`](std::f32::NAN) | [`INF`](std::f32::INFINITY). + /// ``` + /// # use umath::FFloat; + /// // SAFETY: i have verified that 7.0 is infact, not NAN or INF. + /// let f = unsafe { FFloat::new(7.0) }; + /// ``` + pub unsafe fn new(from: T) -> Self { + debug_assert!(!from.bad()); + Self(from) + } +} + +impl<T> Deref for FFloat<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for FFloat<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +macro_rules! op { + ($name:ident) => { + impl<T: FastFloat> $name<T> for FFloat<T> { + type Output = FFloat<T>; + + fn $name(self, rhs: T) -> Self::Output { + unsafe { Self::new(T::$name(self.0, rhs)) } + } + } + + impl<T: FastFloat> $name<&T> for FFloat<T> { + type Output = FFloat<T>; + + fn $name(self, rhs: &T) -> Self::Output { + unsafe { Self::new(T::$name(self.0, *rhs)) } + } + } + + impl<T: FastFloat> $name for FFloat<T> { + type Output = FFloat<T>; + fn $name(self, FFloat(rhs): FFloat<T>) -> Self::Output { + unsafe { Self::new(T::$name(self.0, rhs)) } + } + } + + impl<T: FastFloat> $name<&FFloat<T>> for FFloat<T> { + type Output = FFloat<T>; + fn $name(self, FFloat(rhs): &FFloat<T>) -> Self::Output { + unsafe { Self::new(T::$name(self.0, *rhs)) } + } + } + }; +} + +op!(add); +op!(div); +op!(mul); +op!(rem); +op!(sub); + +macro_rules! assign { + ($name:ident, $op:ident) => { + impl<T: FastFloat> $name<T> for FFloat<T> { + fn $name(&mut self, rhs: T) { + *self = unsafe { Self::new(T::$op(self.0, rhs)) }; + } + } + + impl<T: FastFloat> $name<&T> for FFloat<T> { + fn $name(&mut self, rhs: &T) { + *self = unsafe { Self::new(T::$op(self.0, *rhs)) }; + } + } + + impl<T: FastFloat> $name for FFloat<T> { + fn $name(&mut self, FFloat(rhs): FFloat<T>) { + *self = unsafe { Self::new(T::$op(self.0, rhs)) }; + } + } + + impl<T: FastFloat> $name<&FFloat<T>> for FFloat<T> { + fn $name(&mut self, FFloat(rhs): &FFloat<T>) { + *self = unsafe { Self::new(T::$op(self.0, *rhs)) }; + } + } + }; +} +assign!(add_assign, add); +assign!(div_assign, div); +assign!(mul_assign, mul); +assign!(rem_assign, rem); +assign!(sub_assign, sub); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn it_works() { + let result = unsafe { FFloat::new(2.0) + FFloat::new(2.0) }; + assert_eq!(*result, 4.0); + } +} diff --git a/src/trait.rs b/src/trait.rs new file mode 100644 index 0000000..dfe0c21 --- /dev/null +++ b/src/trait.rs @@ -0,0 +1,30 @@ +use std::intrinsics::{ + fadd_fast as add, fdiv_fast as div, fmul_fast as mul, frem_fast as rem, fsub_fast as sub, +}; +macro_rules! meth { + ($($name:ident)|+) => { + pub trait FastFloat: Copy { + $(#[doc(hidden)] unsafe fn $name(a: Self, b: Self) -> Self;)+ + #[doc(hidden)] + fn bad(self) -> bool; + } + + impl FastFloat for f32 { + $(unsafe fn $name(a: Self, b: Self) -> Self { + $name(a, b) + })+ + + + fn bad(self) -> bool { self.is_nan() || self.is_infinite() } + } + + impl FastFloat for f64 { + $(unsafe fn $name(a: Self, b: Self) -> Self { + $name(a, b) + })+ + + fn bad(self) -> bool { self.is_nan() || self.is_infinite() } + } + }; +} +meth!(add | sub | div | mul | rem); |