nan_preserving_float/
lib.rs1use 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}