casper_wasmi_core/
nan_preserving_float.rs

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