reifydb_value/value/
ordered_f64.rs1use std::{
5 cmp::Ordering,
6 fmt,
7 fmt::{Display, Formatter},
8 hash::{Hash, Hasher},
9 ops::Deref,
10};
11
12use serde::{Deserialize, Deserializer, Serialize, Serializer, de, de::Visitor};
13
14use crate::{
15 error::{Error, TypeError},
16 util::float_format::format_f64,
17};
18
19#[repr(transparent)]
20#[derive(Debug, Copy, Clone, Default)]
21pub struct OrderedF64(f64);
22
23impl Serialize for OrderedF64 {
24 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
25 where
26 S: Serializer,
27 {
28 serializer.serialize_f64(self.0)
29 }
30}
31
32impl<'de> Deserialize<'de> for OrderedF64 {
33 fn deserialize<D>(deserializer: D) -> Result<OrderedF64, D::Error>
34 where
35 D: Deserializer<'de>,
36 {
37 struct F64Visitor;
38
39 impl Visitor<'_> for F64Visitor {
40 type Value = OrderedF64;
41
42 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
43 formatter.write_str("a 64-bit floating point number")
44 }
45
46 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
47 where
48 E: de::Error,
49 {
50 OrderedF64::try_from(value)
51 .map_err(|e| E::custom(format_args!("OrderedF64: {}", e.diagnostic().message)))
52 }
53
54 fn visit_f32<E>(self, value: f32) -> Result<Self::Value, E>
55 where
56 E: de::Error,
57 {
58 OrderedF64::try_from(value as f64)
59 .map_err(|e| E::custom(format_args!("OrderedF64: {}", e.diagnostic().message)))
60 }
61 }
62
63 deserializer.deserialize_f64(F64Visitor)
64 }
65}
66
67impl OrderedF64 {
68 pub fn value(&self) -> f64 {
69 self.0
70 }
71
72 pub fn zero() -> OrderedF64 {
73 OrderedF64(0.0f64)
74 }
75
76 pub fn canonical(f: f64) -> f64 {
77 if f.is_nan() {
78 f64::NAN
79 } else if f == 0.0 {
80 0.0
81 } else {
82 f
83 }
84 }
85}
86
87impl Deref for OrderedF64 {
88 type Target = f64;
89
90 fn deref(&self) -> &Self::Target {
91 &self.0
92 }
93}
94
95impl Display for OrderedF64 {
96 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
97 f.write_str(&format_f64(self.0))
98 }
99}
100
101impl PartialEq for OrderedF64 {
102 fn eq(&self, other: &Self) -> bool {
103 self.0.to_bits() == other.0.to_bits()
104 }
105}
106
107impl Eq for OrderedF64 {}
108
109impl PartialOrd for OrderedF64 {
110 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
111 Some(self.cmp(other))
112 }
113}
114
115impl Ord for OrderedF64 {
116 fn cmp(&self, other: &Self) -> Ordering {
117 let l = float_to_ordered_u64(self.0);
118 let r = float_to_ordered_u64(other.0);
119 l.cmp(&r)
120 }
121}
122
123#[inline]
124fn float_to_ordered_u64(f: f64) -> u64 {
125 let bits = f.to_bits();
126 if bits & 0x8000000000000000 == 0 {
127 bits ^ 0x8000000000000000
128 } else {
129 !bits
130 }
131}
132
133impl Hash for OrderedF64 {
134 fn hash<H: Hasher>(&self, state: &mut H) {
135 self.0.to_bits().hash(state);
136 }
137}
138
139impl From<OrderedF64> for f64 {
140 fn from(v: OrderedF64) -> Self {
141 v.0
142 }
143}
144
145impl TryFrom<f64> for OrderedF64 {
146 type Error = Error;
147
148 fn try_from(f: f64) -> Result<Self, Self::Error> {
149 if f.is_nan() {
150 Err(TypeError::NanNotAllowed.into())
151 } else {
152 Ok(OrderedF64(Self::canonical(f)))
153 }
154 }
155}
156
157#[cfg(test)]
158#[allow(clippy::approx_constant)]
159pub mod tests {
160 use std::{collections::HashSet, convert::TryFrom};
161
162 use super::*;
163
164 #[test]
165 fn test_eq_and_ord() {
166 let a = OrderedF64::try_from(3.14).unwrap();
167 let b = OrderedF64::try_from(3.14).unwrap();
168 let c = OrderedF64::try_from(2.71).unwrap();
169
170 assert_eq!(a, b);
171 assert!(a > c);
172 assert!(c < a);
173 }
174
175 #[test]
176 fn test_sorting() {
177 let mut values = vec![
178 OrderedF64::try_from(10.0).unwrap(),
179 OrderedF64::try_from(2.0).unwrap(),
180 OrderedF64::try_from(5.0).unwrap(),
181 ];
182 values.sort();
183 let sorted: Vec<f64> = values.into_iter().map(|v| v.0).collect();
184 assert_eq!(sorted, vec![2.0, 5.0, 10.0]);
185 }
186
187 #[test]
188 fn test_hash_eq() {
189 let a = OrderedF64::try_from(1.0).unwrap();
190 let b = OrderedF64::try_from(1.0).unwrap();
191
192 let mut set = HashSet::new();
193 set.insert(a);
194 assert!(set.contains(&b));
195 }
196
197 #[test]
198 fn test_normalizes_zero() {
199 let pos_zero = OrderedF64::try_from(0.0).unwrap();
200 let neg_zero = OrderedF64::try_from(-0.0).unwrap();
201
202 assert_eq!(pos_zero, neg_zero);
203
204 let mut set = HashSet::new();
205 set.insert(pos_zero);
206 assert!(set.contains(&neg_zero));
207 }
208
209 #[test]
210 fn test_nan_fails() {
211 assert!(OrderedF64::try_from(f64::NAN).is_err());
212 }
213
214 #[test]
215 fn test_negative_less_than_positive() {
216 let neg = OrderedF64::try_from(-1.5).unwrap();
217 let pos = OrderedF64::try_from(1.5).unwrap();
218 assert!(neg < pos);
219 }
220
221 #[test]
222 fn test_negative_less_than_zero() {
223 let neg = OrderedF64::try_from(-0.0001).unwrap();
224 let zero = OrderedF64::try_from(0.0).unwrap();
225 assert!(neg < zero);
226 }
227
228 #[test]
229 fn test_sorting_with_negatives() {
230 let mut values = vec![
231 OrderedF64::try_from(3.14).unwrap(),
232 OrderedF64::try_from(-1.5).unwrap(),
233 OrderedF64::try_from(0.0).unwrap(),
234 OrderedF64::try_from(99999.0).unwrap(),
235 OrderedF64::try_from(-100.0).unwrap(),
236 ];
237 values.sort();
238 let sorted: Vec<f64> = values.into_iter().map(|v| v.0).collect();
239 assert_eq!(sorted, vec![-100.0, -1.5, 0.0, 3.14, 99999.0]);
240 }
241}