typed_floats/types/impls/
hash.rs
1use crate::{
2 Negative, NegativeFinite, NonNaN, NonNaNFinite, NonZeroNonNaN, NonZeroNonNaNFinite, Positive,
3 PositiveFinite, StrictlyNegative, StrictlyNegativeFinite, StrictlyPositive,
4 StrictlyPositiveFinite,
5};
6
7impl core::hash::Hash for NonNaN<f32> {
13 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
14 (self.0 + 0.0).to_bits().hash(state);
17 }
18}
19
20impl core::hash::Hash for NonNaN<f64> {
21 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
22 (self.0 + 0.0).to_bits().hash(state);
25 }
26}
27
28impl core::hash::Hash for NonNaNFinite<f32> {
29 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
30 (self.0 + 0.0).to_bits().hash(state);
33 }
34}
35
36impl core::hash::Hash for NonNaNFinite<f64> {
37 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
38 (self.0 + 0.0).to_bits().hash(state);
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::*;
47
48 #[test]
49 fn zero_optimization() {
50 let zero_f32 = 0.0f32;
51 let zero_f64 = 0.0f64;
52
53 let neg_zero_f32 = -0.0f32;
54 let neg_zero_f64 = -0.0f64;
55
56 let sum_f32 = zero_f32 + neg_zero_f32;
57 let sum_f64 = zero_f64 + neg_zero_f64;
58
59 assert!(sum_f32.is_sign_positive());
60 assert!(sum_f64.is_sign_positive());
61
62 let values = tf32::get_test_values();
63 for value in values {
64 if value != 0.0 && !value.is_nan() {
65 let sum = value + 0.0;
66 assert_eq!(sum, value);
67 assert_eq!(sum.to_bits(), value.to_bits());
68 assert_eq!(sum.is_sign_positive(), value.is_sign_positive());
69 }
70 }
71
72 let values = tf64::get_test_values();
73 for value in values {
74 if value != 0.0 && !value.is_nan() {
75 let sum = value + 0.0;
76 assert_eq!(sum, value);
77 assert_eq!(sum.to_bits(), value.to_bits());
78 assert_eq!(sum.is_sign_positive(), value.is_sign_positive());
79 }
80 }
81 }
82}
83
84macro_rules! impl_hash_test {
85 ($test:ident, $type:ident) => {
86 #[cfg(test)]
87 mod $test {
88 extern crate std;
89 use crate::*;
90 use std::vec::Vec; #[test]
93 fn f32() {
94 let mut hash_set = std::collections::HashSet::new();
95
96 let neg_zero = $type::<f32>::new(-0.0);
97 let pos_zero = $type::<f32>::new(0.0);
98 let accept_both_zeroes = neg_zero.is_ok() && pos_zero.is_ok();
99 if accept_both_zeroes {
100 let pos_one = $type::<f32>::new(1.0);
101 let neg_one = $type::<f32>::new(-1.0);
102
103 hash_set.insert(neg_zero.unwrap());
104 hash_set.insert(pos_zero.unwrap());
105 let mut count = 1; assert_eq!(hash_set.len(), count);
107
108 if pos_one.is_ok() {
109 hash_set.insert(pos_one.unwrap());
110 count += 1;
111 }
112
113 if neg_one.is_ok() {
114 hash_set.insert(neg_one.unwrap());
115 count += 1;
116 }
117
118 assert_eq!(hash_set.len(), count);
119 }
120
121 let values = tf32::get_test_values()
122 .iter()
123 .map(|&x| tf32::$type::new(x))
124 .filter_map(|x| x.ok())
125 .collect::<Vec<_>>();
126
127 let mut distincs = Vec::new();
128 for x in values.iter() {
129 if !distincs.contains(x) {
130 distincs.push(*x);
131 }
132 }
133
134 let mut hash_set = std::collections::HashSet::new();
135
136 for value in &values {
137 hash_set.insert(value);
138 }
139
140 assert_eq!(hash_set.len(), distincs.len());
141 }
142
143 #[test]
144 fn f64() {
145 let mut hash_set = std::collections::HashSet::new();
146
147 let neg_zero = $type::<f64>::new(-0.0);
148 let pos_zero = $type::<f64>::new(0.0);
149 let accept_both_zeroes = neg_zero.is_ok() && pos_zero.is_ok();
150 if accept_both_zeroes {
151 let pos_one = $type::<f64>::new(1.0);
152 let neg_one = $type::<f64>::new(-1.0);
153
154 hash_set.insert(neg_zero.unwrap());
155 hash_set.insert(pos_zero.unwrap());
156 let mut count = 1; assert_eq!(hash_set.len(), count);
158
159 if pos_one.is_ok() {
160 hash_set.insert(pos_one.unwrap());
161 count += 1;
162 }
163
164 if neg_one.is_ok() {
165 hash_set.insert(neg_one.unwrap());
166 count += 1;
167 }
168
169 assert_eq!(hash_set.len(), count);
170 }
171
172 let values = tf64::get_test_values()
173 .iter()
174 .map(|&x| tf64::$type::new(x))
175 .filter_map(|x| x.ok())
176 .collect::<Vec<_>>();
177
178 let mut distincs = Vec::new();
179 for x in values.iter() {
180 if !distincs.contains(x) {
181 distincs.push(*x);
182 }
183 }
184
185 let mut hash_set = std::collections::HashSet::new();
186
187 for value in &values {
188 hash_set.insert(value);
189 }
190
191 assert_eq!(hash_set.len(), distincs.len());
192 }
193 }
194 };
195}
196
197macro_rules! impl_hash {
198 ($test:ident, $type:ident) => {
199 impl core::hash::Hash for $type<f32> {
200 #[inline]
201 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
202 self.0.to_bits().hash(state);
203 }
204 }
205
206 impl core::hash::Hash for $type<f64> {
207 #[inline]
208 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
209 self.0.to_bits().hash(state);
210 }
211 }
212
213 impl_hash_test!($test, $type);
214 };
215}
216
217impl_hash!(non_zero_non_nan, NonZeroNonNaN);
218impl_hash!(non_zero_non_nan_finite, NonZeroNonNaNFinite);
219impl_hash!(positive, Positive);
220impl_hash!(negative, Negative);
221impl_hash!(positive_finite, PositiveFinite);
222impl_hash!(negative_finite, NegativeFinite);
223impl_hash!(strictly_positive, StrictlyPositive);
224impl_hash!(strictly_negative, StrictlyNegative);
225impl_hash!(strictly_positive_finite, StrictlyPositiveFinite);
226impl_hash!(strictly_negative_finite, StrictlyNegativeFinite);
227
228impl_hash_test!(non_nan, NonNaN);
229impl_hash_test!(non_nan_finite, NonNaNFinite);