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#[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 #[inline]
46 #[must_use]
47 pub fn abs(self) -> Self {
48 self.value.abs().into()
49 }
50
51 #[inline]
53 #[must_use]
54 pub fn ceil(self) -> Self {
55 self.value.ceil().into()
56 }
57
58 #[inline]
60 #[must_use]
61 pub fn floor(self) -> Self {
62 self.value.floor().into()
63 }
64
65 #[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 #[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}