1use std::cmp::Ordering;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4
5#[derive(Debug, Default, Clone, Copy)]
6pub struct F32(pub f32);
7
8impl PartialEq for F32 {
10 fn eq(&self, other: &Self) -> bool {
11 if self.0.is_nan() && other.0.is_nan() {
12 true
13 } else {
14 self.0 == other.0
15 }
16 }
17}
18
19impl Eq for F32 {}
20
21impl PartialOrd for F32 {
24 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
25 Some(self.cmp(other))
26 }
27}
28
29impl Ord for F32 {
32 fn cmp(&self, other: &Self) -> Ordering {
33 self.0.partial_cmp(&other.0).unwrap_or_else(|| {
34 if self.0.is_nan() && !other.0.is_nan() {
35 Ordering::Less
36 } else if !self.0.is_nan() && other.0.is_nan() {
37 Ordering::Greater
38 } else {
39 Ordering::Equal
40 }
41 })
42 }
43}
44
45impl Hash for F32 {
46 fn hash<H: Hasher>(&self, state: &mut H) {
47 if self.0.is_nan() {
48 0x7fc00000u32.hash(state); } else if self.0 == 0.0 { 0u32.hash(state);
51 } else {
52 self.0.to_bits().hash(state);
53 }
54 }
55}
56
57impl From<F32> for f32 {
58 fn from(f: F32) -> Self {
59 f.0
60 }
61}
62
63impl From<f32> for F32 {
64 fn from(f: f32) -> Self {
65 F32(f)
66 }
67}
68
69impl fmt::Display for F32 {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 self.0.fmt(f)
72 }
73}
74
75#[derive(Debug, Default, Clone, Copy)]
76pub struct F64(pub f64);
77
78impl PartialEq for F64 {
80 fn eq(&self, other: &Self) -> bool {
81 if self.0.is_nan() && other.0.is_nan() {
82 true
83 } else {
84 self.0 == other.0
85 }
86 }
87}
88
89impl Eq for F64 {}
90
91impl PartialOrd for F64 {
94 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
95 Some(self.cmp(other))
96 }
97}
98
99impl Ord for F64 {
102 fn cmp(&self, other: &Self) -> Ordering {
103 self.0.partial_cmp(&other.0).unwrap_or_else(|| {
104 if self.0.is_nan() && !other.0.is_nan() {
105 Ordering::Less
106 } else if !self.0.is_nan() && other.0.is_nan() {
107 Ordering::Greater
108 } else {
109 Ordering::Equal
110 }
111 })
112 }
113}
114
115impl Hash for F64 {
116 fn hash<H: Hasher>(&self, state: &mut H) {
117 if self.0.is_nan() {
118 0x7ff8000000000000u64.hash(state); } else if self.0 == 0.0 { 0u64.hash(state);
121 } else {
122 self.0.to_bits().hash(state);
123 }
124 }
125}
126
127impl From<F64> for f64 {
128 fn from(f: F64) -> Self {
129 f.0
130 }
131}
132
133impl From<f64> for F64 {
134 fn from(f: f64) -> Self {
135 F64(f)
136 }
137}
138
139impl fmt::Display for F64 {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 self.0.fmt(f)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use std::collections::hash_map::DefaultHasher;
148 use std::hash::{Hash, Hasher};
149
150 use super::{F32, F64};
151
152 fn calculate_hash<T: Hash>(t: &T) -> u64 {
153 let mut s = DefaultHasher::new();
154 t.hash(&mut s);
155 s.finish()
156 }
157
158 #[test]
159 fn f32_eq() {
160 assert!(F32(std::f32::NAN) == F32(std::f32::NAN));
161 assert!(F32(std::f32::NAN) != F32(5.0));
162 assert!(F32(5.0) != F32(std::f32::NAN));
163 assert!(F32(0.0) == F32(-0.0));
164 }
165
166 #[test]
167 fn f32_cmp() {
168 assert!(F32(std::f32::NAN) == F32(std::f32::NAN));
169 assert!(F32(std::f32::NAN) < F32(5.0));
170 assert!(F32(5.0) > F32(std::f32::NAN));
171 assert!(F32(0.0) == F32(-0.0));
172 }
173
174 #[test]
175 fn f32_hash() {
176 assert!(calculate_hash(&F32(0.0)) == calculate_hash(&F32(-0.0)));
177 assert!(calculate_hash(&F32(std::f32::NAN)) == calculate_hash(&F32(-std::f32::NAN)));
178 }
179
180 #[test]
181 fn f64_eq() {
182 assert!(F64(std::f64::NAN) == F64(std::f64::NAN));
183 assert!(F64(std::f64::NAN) != F64(5.0));
184 assert!(F64(5.0) != F64(std::f64::NAN));
185 assert!(F64(0.0) == F64(-0.0));
186 }
187
188 #[test]
189 fn f64_cmp() {
190 assert!(F64(std::f64::NAN) == F64(std::f64::NAN));
191 assert!(F64(std::f64::NAN) < F64(5.0));
192 assert!(F64(5.0) > F64(std::f64::NAN));
193 assert!(F64(0.0) == F64(-0.0));
194 }
195
196 #[test]
197 fn f64_hash() {
198 assert!(calculate_hash(&F64(0.0)) == calculate_hash(&F64(-0.0)));
199 assert!(calculate_hash(&F64(std::f64::NAN)) == calculate_hash(&F64(-std::f64::NAN)));
200 }
201}