nan_preserving_float/
lib.rs

1use std::ops::{Add, Div, Mul, Neg, Sub, Rem};
2use std::cmp::{Ordering, PartialEq, PartialOrd};
3
4macro_rules! impl_binop {
5    ($for:ident, $is:ident, $op:ident, $func_name:ident) => {
6        impl<T: Into<$for>> $op<T> for $for {
7            type Output = Self;
8
9            fn $func_name(self, other: T) -> Self {
10                $for(
11                    $op::$func_name(
12                        $is::from_bits(self.0),
13                        $is::from_bits(other.into().0)
14                    ).to_bits()
15                )
16            }
17        }
18    }
19}
20
21macro_rules! float {
22    ($for:ident, $rep:ident, $is:ident) => {
23        float!($for, $rep, $is, 1 << (::std::mem::size_of::<$is>() * 8 - 1));
24    };
25    ($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => {
26        #[derive(Copy, Clone)]
27        pub struct $for($rep);
28
29        impl_binop!($for, $is, Add, add);
30        impl_binop!($for, $is, Sub, sub);
31        impl_binop!($for, $is, Mul, mul);
32        impl_binop!($for, $is, Div, div);
33        impl_binop!($for, $is, Rem, rem);
34
35        impl $for {
36            pub fn from_bits(other: $rep) -> Self {
37                $for(other)
38            }
39
40            pub fn to_bits(self) -> $rep {
41                self.0
42            }
43
44            pub fn from_float(fl: $is) -> Self {
45                fl.into()
46            }
47
48            pub fn to_float(self) -> $is {
49                self.into()
50            }
51
52            pub fn is_nan(self) -> bool {
53                self.to_float().is_nan()
54            }
55
56            pub fn abs(self) -> Self {
57                $for(self.0 & !$sign_bit)
58            }
59
60            pub fn fract(self) -> Self {
61                self.to_float().fract().into()
62            }
63
64            pub fn min(self, other: Self) -> Self {
65                Self::from(self.to_float().min(other.to_float()))
66            }
67
68            pub fn max(self, other: Self) -> Self {
69                Self::from(self.to_float().max(other.to_float()))
70            }
71        }
72
73        impl From<$is> for $for {
74            fn from(other: $is) -> $for {
75                $for(other.to_bits())
76            }
77        }
78
79        impl From<$for> for $is {
80            fn from(other: $for) -> $is {
81                <$is>::from_bits(other.0)
82            }
83        }
84
85        impl Neg for $for {
86            type Output = Self;
87
88            fn neg(self) -> Self {
89                $for(self.0 ^ $sign_bit)
90            }
91        }
92
93        impl<T: Into<$for> + Copy> PartialEq<T> for $for {
94            fn eq(&self, other: &T) -> bool {
95                $is::from(*self) == $is::from((*other).into())
96            }
97        }
98
99        impl<T: Into<$for> + Copy> PartialOrd<T> for $for {
100            fn partial_cmp(&self, other: &T) -> Option<Ordering> {
101                $is::from(*self).partial_cmp(&$is::from((*other).into()))
102            }
103        }
104
105        impl ::std::fmt::Debug for $for {
106            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
107                $is::from(*self).fmt(f)
108            }
109        }
110    }
111}
112
113float!(F32, u32, f32);
114float!(F64, u64, f64);
115
116impl From<u32> for F32 {
117    fn from(other: u32) -> Self {
118        Self::from_bits(other)
119    }
120}
121
122impl From<F32> for u32 {
123    fn from(other: F32) -> Self {
124        other.to_bits()
125    }
126}
127
128impl From<u64> for F64 {
129    fn from(other: u64) -> Self {
130        Self::from_bits(other)
131    }
132}
133
134impl From<F64> for u64 {
135    fn from(other: F64) -> Self {
136        other.to_bits()
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    extern crate rand;
143
144    use self::rand::Rng;
145
146    use super::{F32, F64};
147
148    use std::ops::{Add, Div, Mul, Neg, Sub};
149    use std::fmt::Debug;
150    use std::iter;
151
152    fn test_ops<T, F, I>(iter: I)
153    where
154        T: Add<Output = T>
155            + Div<Output = T>
156            + Mul<Output = T>
157            + Sub<Output = T>
158            + Neg<Output = T>
159            + Copy
160            + Debug
161            + PartialEq,
162        F: Into<T>
163            + Add<Output = F>
164            + Div<Output = F>
165            + Mul<Output = F>
166            + Sub<Output = F>
167            + Neg<Output = F>
168            + Copy
169            + Debug,
170        I: IntoIterator<Item = (F, F)>,
171    {
172        for (a, b) in iter {
173            assert_eq!((a + b).into(), a.into() + b.into());
174            assert_eq!((a - b).into(), a.into() - b.into());
175            assert_eq!((a * b).into(), a.into() * b.into());
176            assert_eq!((a / b).into(), a.into() / b.into());
177            assert_eq!((-a).into(), -a.into());
178            assert_eq!((-b).into(), -b.into());
179        }
180    }
181
182    #[test]
183    fn test_ops_f32() {
184        let mut rng = rand::thread_rng();
185        let iter = iter::repeat(()).map(|_| rng.gen());
186
187        test_ops::<F32, f32, _>(iter.take(1000));
188    }
189
190    #[test]
191    fn test_ops_f64() {
192        let mut rng = rand::thread_rng();
193        let iter = iter::repeat(()).map(|_| rng.gen());
194
195        test_ops::<F64, f64, _>(iter.take(1000));
196    }
197
198    #[test]
199    fn test_neg_nan_f32() {
200        assert_eq!((-F32(0xff80_3210)).0, 0x7f80_3210);
201    }
202
203    #[test]
204    fn test_neg_nan_f64() {
205        assert_eq!((-F64(0xff80_3210_0000_0000)).0, 0x7f80_3210_0000_0000);
206    }
207}