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 Ok(OrderedF64(value))
48 }
49
50 fn visit_f32<E>(self, value: f32) -> Result<Self::Value, E>
51 where
52 E: de::Error,
53 {
54 Ok(OrderedF64(value as f64))
55 }
56 }
57
58 deserializer.deserialize_f64(F64Visitor)
59 }
60}
61
62impl OrderedF64 {
63 pub fn value(&self) -> f64 {
64 self.0
65 }
66
67 pub fn zero() -> OrderedF64 {
68 OrderedF64(0.0f64)
69 }
70}
71
72impl Deref for OrderedF64 {
73 type Target = f64;
74
75 fn deref(&self) -> &Self::Target {
76 &self.0
77 }
78}
79
80impl Display for OrderedF64 {
81 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
82 f.write_str(&format_f64(self.0))
83 }
84}
85
86impl PartialEq for OrderedF64 {
87 fn eq(&self, other: &Self) -> bool {
88 self.0.to_bits() == other.0.to_bits()
89 }
90}
91
92impl Eq for OrderedF64 {}
93
94impl PartialOrd for OrderedF64 {
95 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
96 Some(self.cmp(other))
97 }
98}
99
100impl Ord for OrderedF64 {
101 fn cmp(&self, other: &Self) -> Ordering {
102 let l = float_to_ordered_u64(self.0);
103 let r = float_to_ordered_u64(other.0);
104 l.cmp(&r)
105 }
106}
107
108#[inline]
109fn float_to_ordered_u64(f: f64) -> u64 {
110 let bits = f.to_bits();
111 if bits & 0x8000000000000000 == 0 {
112 bits ^ 0x8000000000000000
113 } else {
114 !bits
115 }
116}
117
118impl Hash for OrderedF64 {
119 fn hash<H: Hasher>(&self, state: &mut H) {
120 self.0.to_bits().hash(state);
121 }
122}
123
124impl From<OrderedF64> for f64 {
125 fn from(v: OrderedF64) -> Self {
126 v.0
127 }
128}
129
130impl TryFrom<f64> for OrderedF64 {
131 type Error = Error;
132
133 fn try_from(f: f64) -> Result<Self, Self::Error> {
134 let normalized = if f == 0.0 {
135 0.0
136 } else {
137 f
138 };
139 if f.is_nan() {
140 Err(TypeError::NanNotAllowed.into())
141 } else {
142 Ok(OrderedF64(normalized))
143 }
144 }
145}
146
147#[cfg(test)]
148#[allow(clippy::approx_constant)]
149pub mod tests {
150 use std::{collections::HashSet, convert::TryFrom};
151
152 use super::*;
153
154 #[test]
155 fn test_eq_and_ord() {
156 let a = OrderedF64::try_from(3.14).unwrap();
157 let b = OrderedF64::try_from(3.14).unwrap();
158 let c = OrderedF64::try_from(2.71).unwrap();
159
160 assert_eq!(a, b);
161 assert!(a > c);
162 assert!(c < a);
163 }
164
165 #[test]
166 fn test_sorting() {
167 let mut values = vec![
168 OrderedF64::try_from(10.0).unwrap(),
169 OrderedF64::try_from(2.0).unwrap(),
170 OrderedF64::try_from(5.0).unwrap(),
171 ];
172 values.sort();
173 let sorted: Vec<f64> = values.into_iter().map(|v| v.0).collect();
174 assert_eq!(sorted, vec![2.0, 5.0, 10.0]);
175 }
176
177 #[test]
178 fn test_hash_eq() {
179 let a = OrderedF64::try_from(1.0).unwrap();
180 let b = OrderedF64::try_from(1.0).unwrap();
181
182 let mut set = HashSet::new();
183 set.insert(a);
184 assert!(set.contains(&b));
185 }
186
187 #[test]
188 fn test_normalizes_zero() {
189 let pos_zero = OrderedF64::try_from(0.0).unwrap();
190 let neg_zero = OrderedF64::try_from(-0.0).unwrap();
191
192 assert_eq!(pos_zero, neg_zero);
193
194 let mut set = HashSet::new();
195 set.insert(pos_zero);
196 assert!(set.contains(&neg_zero));
197 }
198
199 #[test]
200 fn test_nan_fails() {
201 assert!(OrderedF64::try_from(f64::NAN).is_err());
202 }
203
204 #[test]
205 fn test_negative_less_than_positive() {
206 let neg = OrderedF64::try_from(-1.5).unwrap();
207 let pos = OrderedF64::try_from(1.5).unwrap();
208 assert!(neg < pos);
209 }
210
211 #[test]
212 fn test_negative_less_than_zero() {
213 let neg = OrderedF64::try_from(-0.0001).unwrap();
214 let zero = OrderedF64::try_from(0.0).unwrap();
215 assert!(neg < zero);
216 }
217
218 #[test]
219 fn test_sorting_with_negatives() {
220 let mut values = vec![
221 OrderedF64::try_from(3.14).unwrap(),
222 OrderedF64::try_from(-1.5).unwrap(),
223 OrderedF64::try_from(0.0).unwrap(),
224 OrderedF64::try_from(99999.0).unwrap(),
225 OrderedF64::try_from(-100.0).unwrap(),
226 ];
227 values.sort();
228 let sorted: Vec<f64> = values.into_iter().map(|v| v.0).collect();
229 assert_eq!(sorted, vec![-100.0, -1.5, 0.0, 3.14, 99999.0]);
230 }
231}