oxsdatatypes/
double.rs

1use crate::{Boolean, Float, Integer};
2use std::cmp::Ordering;
3use std::fmt;
4use std::num::ParseFloatError;
5use std::ops::{Add, Div, Mul, Neg, Sub};
6use std::str::FromStr;
7
8/// [XML Schema `double` datatype](https://www.w3.org/TR/xmlschema11-2/#double)
9///
10/// Uses internally a [`f64`].
11///
12/// <div class="warning">Serialization does not follow the canonical mapping.</div>
13#[derive(Debug, Clone, Copy, Default, PartialEq)]
14#[repr(transparent)]
15pub struct Double {
16    value: f64,
17}
18
19impl Double {
20    pub const INFINITY: Self = Self {
21        value: f64::INFINITY,
22    };
23    pub const MAX: Self = Self { value: f64::MAX };
24    pub const MIN: Self = Self { value: f64::MIN };
25    pub const NAN: Self = Self { value: f64::NAN };
26    pub const NEG_INFINITY: Self = Self {
27        value: f64::NEG_INFINITY,
28    };
29
30    #[inline]
31    #[must_use]
32    pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
33        Self {
34            value: f64::from_be_bytes(bytes),
35        }
36    }
37
38    #[inline]
39    #[must_use]
40    pub fn to_be_bytes(self) -> [u8; 8] {
41        self.value.to_be_bytes()
42    }
43
44    /// [fn:abs](https://www.w3.org/TR/xpath-functions-31/#func-abs)
45    #[inline]
46    #[must_use]
47    pub fn abs(self) -> Self {
48        self.value.abs().into()
49    }
50
51    /// [fn:ceiling](https://www.w3.org/TR/xpath-functions-31/#func-ceiling)
52    #[inline]
53    #[must_use]
54    pub fn ceil(self) -> Self {
55        self.value.ceil().into()
56    }
57
58    /// [fn:floor](https://www.w3.org/TR/xpath-functions-31/#func-floor)
59    #[inline]
60    #[must_use]
61    pub fn floor(self) -> Self {
62        self.value.floor().into()
63    }
64
65    /// [fn:round](https://www.w3.org/TR/xpath-functions-31/#func-round)
66    #[inline]
67    #[must_use]
68    pub fn round(self) -> Self {
69        self.value.round().into()
70    }
71
72    #[inline]
73    #[must_use]
74    pub fn is_nan(self) -> bool {
75        self.value.is_nan()
76    }
77
78    #[inline]
79    #[must_use]
80    pub fn is_finite(self) -> bool {
81        self.value.is_finite()
82    }
83
84    /// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
85    #[inline]
86    #[must_use]
87    pub fn is_identical_with(self, other: Self) -> bool {
88        self.value.to_bits() == other.value.to_bits()
89    }
90}
91
92impl From<Double> for f64 {
93    #[inline]
94    fn from(value: Double) -> Self {
95        value.value
96    }
97}
98
99impl From<f64> for Double {
100    #[inline]
101    fn from(value: f64) -> Self {
102        Self { value }
103    }
104}
105
106impl From<i8> for Double {
107    #[inline]
108    fn from(value: i8) -> Self {
109        Self {
110            value: value.into(),
111        }
112    }
113}
114
115impl From<i16> for Double {
116    #[inline]
117    fn from(value: i16) -> Self {
118        Self {
119            value: value.into(),
120        }
121    }
122}
123
124impl From<i32> for Double {
125    #[inline]
126    fn from(value: i32) -> Self {
127        Self {
128            value: value.into(),
129        }
130    }
131}
132
133impl From<u8> for Double {
134    #[inline]
135    fn from(value: u8) -> Self {
136        Self {
137            value: value.into(),
138        }
139    }
140}
141
142impl From<u16> for Double {
143    #[inline]
144    fn from(value: u16) -> Self {
145        Self {
146            value: value.into(),
147        }
148    }
149}
150
151impl From<u32> for Double {
152    #[inline]
153    fn from(value: u32) -> Self {
154        Self {
155            value: value.into(),
156        }
157    }
158}
159
160impl From<Float> for Double {
161    #[inline]
162    fn from(value: Float) -> Self {
163        Self {
164            value: value.into(),
165        }
166    }
167}
168
169impl From<Boolean> for Double {
170    #[inline]
171    fn from(value: Boolean) -> Self {
172        f64::from(bool::from(value)).into()
173    }
174}
175
176impl From<Integer> for Double {
177    #[inline]
178    #[allow(clippy::cast_precision_loss)]
179    fn from(value: Integer) -> Self {
180        (i64::from(value) as f64).into()
181    }
182}
183
184impl FromStr for Double {
185    type Err = ParseFloatError;
186
187    #[inline]
188    fn from_str(input: &str) -> Result<Self, Self::Err> {
189        Ok(f64::from_str(input)?.into())
190    }
191}
192
193impl fmt::Display for Double {
194    #[inline]
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        if self.value == f64::INFINITY {
197            f.write_str("INF")
198        } else if self.value == f64::NEG_INFINITY {
199            f.write_str("-INF")
200        } else {
201            self.value.fmt(f)
202        }
203    }
204}
205
206impl PartialOrd for Double {
207    #[inline]
208    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
209        self.value.partial_cmp(&other.value)
210    }
211}
212
213impl Neg for Double {
214    type Output = Self;
215
216    #[inline]
217    fn neg(self) -> Self {
218        (-self.value).into()
219    }
220}
221
222impl Add for Double {
223    type Output = Self;
224
225    #[inline]
226    fn add(self, rhs: Self) -> Self {
227        (self.value + rhs.value).into()
228    }
229}
230
231impl Sub for Double {
232    type Output = Self;
233
234    #[inline]
235    fn sub(self, rhs: Self) -> Self {
236        (self.value - rhs.value).into()
237    }
238}
239
240impl Mul for Double {
241    type Output = Self;
242
243    #[inline]
244    fn mul(self, rhs: Self) -> Self {
245        (self.value * rhs.value).into()
246    }
247}
248
249impl Div for Double {
250    type Output = Self;
251
252    #[inline]
253    fn div(self, rhs: Self) -> Self {
254        (self.value / rhs.value).into()
255    }
256}
257
258#[cfg(test)]
259#[allow(clippy::panic_in_result_fn)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn eq() {
265        assert_eq!(Double::from(0_f64), Double::from(0_f64));
266        assert_ne!(Double::NAN, Double::NAN);
267        assert_eq!(Double::from(-0.), Double::from(0.));
268    }
269
270    #[test]
271    fn cmp() {
272        assert_eq!(
273            Double::from(0.).partial_cmp(&Double::from(0.)),
274            Some(Ordering::Equal)
275        );
276        assert_eq!(
277            Double::INFINITY.partial_cmp(&Double::MAX),
278            Some(Ordering::Greater)
279        );
280        assert_eq!(
281            Double::NEG_INFINITY.partial_cmp(&Double::MIN),
282            Some(Ordering::Less)
283        );
284        assert_eq!(Double::NAN.partial_cmp(&Double::from(0.)), None);
285        assert_eq!(Double::NAN.partial_cmp(&Double::NAN), None);
286        assert_eq!(
287            Double::from(0.).partial_cmp(&Double::from(-0.)),
288            Some(Ordering::Equal)
289        );
290    }
291
292    #[test]
293    fn is_identical_with() {
294        assert!(Double::from(0.).is_identical_with(Double::from(0.)));
295        assert!(Double::NAN.is_identical_with(Double::NAN));
296        assert!(!Double::from(-0.).is_identical_with(Double::from(0.)));
297    }
298
299    #[test]
300    fn from_str() -> Result<(), ParseFloatError> {
301        assert_eq!(Double::from_str("NaN")?.to_string(), "NaN");
302        assert_eq!(Double::from_str("INF")?.to_string(), "INF");
303        assert_eq!(Double::from_str("+INF")?.to_string(), "INF");
304        assert_eq!(Double::from_str("-INF")?.to_string(), "-INF");
305        assert_eq!(Double::from_str("0.0E0")?.to_string(), "0");
306        assert_eq!(Double::from_str("-0.0E0")?.to_string(), "-0");
307        assert_eq!(Double::from_str("0.1e1")?.to_string(), "1");
308        assert_eq!(Double::from_str("-0.1e1")?.to_string(), "-1");
309        assert_eq!(Double::from_str("1.e1")?.to_string(), "10");
310        assert_eq!(Double::from_str("-1.e1")?.to_string(), "-10");
311        assert_eq!(Double::from_str("1")?.to_string(), "1");
312        assert_eq!(Double::from_str("-1")?.to_string(), "-1");
313        assert_eq!(Double::from_str("1.")?.to_string(), "1");
314        assert_eq!(Double::from_str("-1.")?.to_string(), "-1");
315        assert_eq!(
316            Double::from_str(&f64::MIN.to_string()).unwrap(),
317            Double::MIN
318        );
319        assert_eq!(
320            Double::from_str(&f64::MAX.to_string()).unwrap(),
321            Double::MAX
322        );
323        Ok(())
324    }
325}