fast_floats/
lib.rs

1//! Experimental (unstable) “fast-math” wrappers for f32, f64
2//!
3//! These wrappers enable the [“fast-math”][1] flags for the operations
4//! where there are intrinsics for this (add, sub, mul, div, rem).
5//! The wrappers exist so that we have a quick & easy way **to experiment**
6//! with fast math flags and further that feature in Rust.
7//!
8//! Note that as of this writing, the Rust instrinsics use the “fast” flag
9//! documented in the langref; this enables all the float flags.
10//!
11//! [1]: http://llvm.org/docs/LangRef.html#fast-math-flags
12//!
13//! # Rust Version
14//!
15//! This crate is nightly only and experimental. Breaking changes can occur at
16//! any time, if changes in Rust require it.
17#![no_std]
18#![feature(core_intrinsics)]
19
20extern crate core as std;
21
22use std::intrinsics::{fadd_fast, fsub_fast, fmul_fast, fdiv_fast, frem_fast};
23use std::ops::{
24    Add,
25    Sub,
26    Mul,
27    Div,
28    Rem,
29    AddAssign,
30    SubAssign,
31    MulAssign,
32    DivAssign,
33    RemAssign,
34};
35
36/// “fast-math” wrapper for f32 and f64.
37///
38/// The `Fast` type enforces no invariant and can hold any f32, f64 values.
39/// See crate docs for more details.
40#[derive(Copy, Clone, PartialEq, PartialOrd)]
41#[repr(transparent)]
42pub struct Fast<F>(F);
43
44/// “fast-math” wrapper for `f64`
45pub type FF64 = Fast<f64>;
46/// “fast-math” wrapper for `f32`
47pub type FF32 = Fast<f32>;
48
49impl<F> Fast<F> {
50    /// Create a new fast value
51    ///
52    /// # Safety
53    ///
54    /// The value can be used with the `fast_fadd` etc intrinsics without checks after it has been
55    /// created like this. Refer to Rust and other sources for documentation on which operations
56    /// are valid (might change with time).
57    ///
58    /// Be wary of operations creating invalid values in `Fast` which they could potentially do
59    /// depending on the operation.
60    pub unsafe fn new(value: F) -> Self { Fast(value) }
61
62    /// Get the inner value
63    pub fn get(self) -> F { self.0 }
64}
65
66impl Into<f32> for Fast<f32> {
67    fn into(self: Self) -> f32 { self.get() }
68}
69
70impl Into<f64> for Fast<f64> {
71    fn into(self: Self) -> f64 { self.get() }
72}
73
74macro_rules! impl_op {
75    ($($name:ident, $method:ident, $intrins:ident;)*) => {
76        $(
77        // Fast<F> + F
78        impl $name<f64> for Fast<f64> {
79            type Output = Self;
80            #[inline(always)]
81            fn $method(self, rhs: f64) -> Self::Output {
82                unsafe {
83                    Fast($intrins(self.0, rhs))
84                }
85            }
86        }
87
88        impl $name<f32> for Fast<f32> {
89            type Output = Self;
90            #[inline(always)]
91            fn $method(self, rhs: f32) -> Self::Output {
92                unsafe {
93                    Fast($intrins(self.0, rhs))
94                }
95            }
96        }
97
98        // F + Fast<F>
99        impl $name<Fast<f64>> for f64 {
100            type Output = Fast<f64>;
101            #[inline(always)]
102            fn $method(self, rhs: Fast<f64>) -> Self::Output {
103                Fast(self).$method(rhs.0)
104            }
105        }
106
107        impl $name<Fast<f32>> for f32 {
108            type Output = Fast<f32>;
109            #[inline(always)]
110            fn $method(self, rhs: Fast<f32>) -> Self::Output {
111                Fast(self).$method(rhs.0)
112            }
113        }
114
115        // Fast<F> + Fast<F>
116        impl $name for Fast<f64> {
117            type Output = Self;
118            #[inline(always)]
119            fn $method(self, rhs: Self) -> Self::Output {
120                self.$method(rhs.0)
121            }
122        }
123
124        impl $name for Fast<f32> {
125            type Output = Self;
126            #[inline(always)]
127            fn $method(self, rhs: Self) -> Self::Output {
128                self.$method(rhs.0)
129            }
130        }
131        )*
132
133    }
134}
135
136macro_rules! impl_assignop {
137    ($($name:ident, $method:ident, $optrt:ident, $opmth:ident;)*) => {
138        $(
139        impl<F, Rhs> $name<Rhs> for Fast<F>
140            where Self: $optrt<Rhs, Output=Self> + Copy,
141        {
142            #[inline(always)]
143            fn $method(&mut self, rhs: Rhs) {
144                *self = (*self).$opmth(rhs)
145            }
146        }
147        )*
148
149    }
150}
151
152impl_op! {
153    Add, add, fadd_fast;
154    Sub, sub, fsub_fast;
155    Mul, mul, fmul_fast;
156    Div, div, fdiv_fast;
157    Rem, rem, frem_fast;
158}
159
160impl_assignop! {
161    AddAssign, add_assign, Add, add;
162    SubAssign, sub_assign, Sub, sub;
163    MulAssign, mul_assign, Mul, mul;
164    DivAssign, div_assign, Div, div;
165    RemAssign, rem_assign, Rem, rem;
166}
167
168use std::fmt;
169macro_rules! impl_format {
170    ($($name:ident)+) => {
171        $(
172        impl<F: fmt::$name> fmt::$name for Fast<F> {
173            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174                self.0.fmt(f)
175            }
176        }
177        )+
178    }
179}
180
181impl_format!(Debug Display LowerExp UpperExp);
182
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187
188    macro_rules! test_op {
189        ($($op:tt)+) => {
190            $(
191                assert_eq!(Fast(2.) $op Fast(1.), Fast(2. $op 1.));
192            )+
193        }
194    }
195
196    #[test]
197    fn each_op() {
198        test_op!(+ - * / %);
199    }
200
201    macro_rules! assign_op {
202        ($($x:literal $op:tt $y:literal is $z:literal ;)+) => {
203            $(
204                let mut x = Fast($x);
205                x $op Fast($y);
206                assert_eq!(x, Fast($z));
207            )+
208        }
209    }
210
211    #[test]
212    fn assign_ops() {
213        assign_op!(
214            1. += 2. is 3.;
215            1. -= 2. is -1.;
216            2. *= 2. is 4.;
217            2. /= 2. is 1.;
218            5. %= 2. is 1.;
219        );
220    }
221}