relay_protocol/
finite.rs

1use std::cmp::Ordering;
2use std::error::Error;
3use std::hash::{Hash, Hasher};
4use std::num::ParseFloatError;
5use std::str::FromStr;
6use std::{fmt, ops};
7
8use serde::{Deserialize, Serialize};
9
10/// A finite 64-bit floating point type.
11///
12/// This is a restricted version of [`f64`] that does not allow NaN or infinity.
13#[derive(Clone, Copy, Default, PartialEq, Deserialize, Serialize)]
14#[serde(try_from = "f64", into = "f64")]
15#[repr(transparent)]
16pub struct FiniteF64(f64);
17
18impl FiniteF64 {
19    /// Largest finite value.
20    pub const MAX: Self = Self(f64::MAX);
21    /// Smallest finite value.
22    pub const MIN: Self = Self(f64::MIN);
23    /// Smallest positive normal value.
24    pub const EPSILON: Self = Self(f64::EPSILON);
25    /// Zero.
26    pub const ZERO: Self = Self(0.0);
27
28    /// Creates a finite float without checking whether the value is finte. This results in
29    /// undefined behavior if the value is non-finite.
30    ///
31    /// # Safety
32    ///
33    /// The value must not be NaN or infinite.
34    #[must_use]
35    #[inline]
36    pub const unsafe fn new_unchecked(value: f64) -> Self {
37        Self(value)
38    }
39
40    /// Creates a finite float if the value is finite.
41    #[must_use]
42    #[inline]
43    pub fn new(value: f64) -> Option<Self> {
44        if value.is_finite() {
45            Some(Self(value))
46        } else {
47            None
48        }
49    }
50
51    /// Returns the plain [`f64`].
52    #[inline]
53    pub const fn to_f64(self) -> f64 {
54        self.0
55    }
56
57    /// Computes the absolute value of self.
58    pub fn abs(self) -> Self {
59        Self(self.0.abs())
60    }
61
62    /// Returns the maximum of two numbers.
63    pub fn max(self, other: Self) -> Self {
64        Self(self.0.max(other.0))
65    }
66
67    /// Returns the minimum of two numbers.
68    pub fn min(self, other: Self) -> Self {
69        Self(self.0.min(other.0))
70    }
71
72    /// Adds two numbers, saturating at the maximum and minimum representable values.
73    pub fn saturating_add(self, other: Self) -> Self {
74        Self((self.0 + other.0).clamp(f64::MIN, f64::MAX))
75    }
76
77    /// Subtracts two numbers, saturating at the maximum and minimum representable values.
78    pub fn saturating_sub(self, other: Self) -> Self {
79        Self((self.0 - other.0).clamp(f64::MIN, f64::MAX))
80    }
81
82    /// Multiplies two numbers, saturating at the maximum and minimum representable values.
83    pub fn saturating_mul(self, other: Self) -> Self {
84        Self((self.0 * other.0).clamp(f64::MIN, f64::MAX))
85    }
86
87    /// Divides two numbers, saturating at the maximum and minimum representable values.
88    pub fn saturating_div(self, other: Self) -> Self {
89        Self((self.0 / other.0).clamp(f64::MIN, f64::MAX))
90    }
91
92    // NB: There is no saturating_div, since 0/0 is NaN, which is not finite.
93}
94
95impl Eq for FiniteF64 {}
96
97impl PartialOrd for FiniteF64 {
98    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
99        Some(self.cmp(other))
100    }
101}
102
103impl Ord for FiniteF64 {
104    fn cmp(&self, other: &Self) -> Ordering {
105        // Safety: NaN and infinity cannot be constructed from a finite f64.
106        self.0.partial_cmp(&other.0).unwrap_or(Ordering::Less)
107    }
108}
109
110impl PartialEq<f64> for FiniteF64 {
111    fn eq(&self, other: &f64) -> bool {
112        &self.0 == other
113    }
114}
115
116impl PartialOrd<f64> for FiniteF64 {
117    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
118        self.0.partial_cmp(other)
119    }
120}
121
122impl Hash for FiniteF64 {
123    fn hash<H: Hasher>(&self, state: &mut H) {
124        // Safety: NaN and infinity cannot be constructed from a finite f64.
125        self.0.to_bits().hash(state)
126    }
127}
128
129impl fmt::Debug for FiniteF64 {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        self.0.fmt(f)
132    }
133}
134
135impl fmt::Display for FiniteF64 {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        self.0.fmt(f)
138    }
139}
140
141impl ops::Add for FiniteF64 {
142    type Output = Option<Self>;
143
144    fn add(self, other: Self) -> Option<Self> {
145        Self::new(self.0 + other.0)
146    }
147}
148
149impl ops::Sub for FiniteF64 {
150    type Output = Option<Self>;
151
152    fn sub(self, other: Self) -> Option<Self> {
153        Self::new(self.0 - other.0)
154    }
155}
156
157impl ops::Mul for FiniteF64 {
158    type Output = Option<Self>;
159
160    fn mul(self, other: Self) -> Option<Self> {
161        Self::new(self.0 * other.0)
162    }
163}
164
165impl ops::Div for FiniteF64 {
166    type Output = Option<Self>;
167
168    fn div(self, other: Self) -> Option<Self> {
169        Self::new(self.0 / other.0)
170    }
171}
172
173impl ops::Rem for FiniteF64 {
174    type Output = Option<Self>;
175
176    fn rem(self, other: Self) -> Option<Self> {
177        Self::new(self.0 % other.0)
178    }
179}
180
181impl ops::AddAssign for FiniteF64 {
182    fn add_assign(&mut self, rhs: Self) {
183        *self = self.saturating_add(rhs)
184    }
185}
186
187/// Error type returned when conversion to [`FiniteF64`] fails.
188#[derive(Debug, thiserror::Error)]
189#[error("float is nan or infinite")]
190pub struct TryFromFloatError;
191
192impl TryFrom<f64> for FiniteF64 {
193    type Error = TryFromFloatError;
194
195    fn try_from(value: f64) -> Result<Self, Self::Error> {
196        Self::new(value).ok_or(TryFromFloatError)
197    }
198}
199
200impl TryFrom<f32> for FiniteF64 {
201    type Error = TryFromFloatError;
202
203    fn try_from(value: f32) -> Result<Self, Self::Error> {
204        f64::from(value).try_into()
205    }
206}
207
208impl From<i8> for FiniteF64 {
209    fn from(value: i8) -> Self {
210        unsafe { Self::new_unchecked(value.into()) }
211    }
212}
213
214impl From<i16> for FiniteF64 {
215    fn from(value: i16) -> Self {
216        unsafe { Self::new_unchecked(value.into()) }
217    }
218}
219
220impl From<i32> for FiniteF64 {
221    fn from(value: i32) -> Self {
222        unsafe { Self::new_unchecked(value.into()) }
223    }
224}
225
226impl From<u8> for FiniteF64 {
227    fn from(value: u8) -> Self {
228        unsafe { Self::new_unchecked(value.into()) }
229    }
230}
231
232impl From<u16> for FiniteF64 {
233    fn from(value: u16) -> Self {
234        unsafe { Self::new_unchecked(value.into()) }
235    }
236}
237
238impl From<u32> for FiniteF64 {
239    fn from(value: u32) -> Self {
240        unsafe { Self::new_unchecked(value.into()) }
241    }
242}
243
244impl From<FiniteF64> for f64 {
245    fn from(value: FiniteF64) -> Self {
246        value.to_f64()
247    }
248}
249
250impl From<TryFromFloatError> for crate::Error {
251    fn from(value: TryFromFloatError) -> Self {
252        Self::invalid(value)
253    }
254}
255
256#[derive(Debug)]
257enum ParseFiniteFloatErrorKind {
258    Invalid(ParseFloatError),
259    NonFinite(TryFromFloatError),
260}
261
262/// Error type returned when parsing [`FiniteF64`] fails.
263#[derive(Debug)]
264pub struct ParseFiniteFloatError(ParseFiniteFloatErrorKind);
265
266impl fmt::Display for ParseFiniteFloatError {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        match &self.0 {
269            ParseFiniteFloatErrorKind::Invalid(err) => err.fmt(f),
270            ParseFiniteFloatErrorKind::NonFinite(err) => err.fmt(f),
271        }
272    }
273}
274
275impl Error for ParseFiniteFloatError {
276    fn source(&self) -> Option<&(dyn Error + 'static)> {
277        match &self.0 {
278            ParseFiniteFloatErrorKind::Invalid(err) => Some(err),
279            ParseFiniteFloatErrorKind::NonFinite(err) => Some(err),
280        }
281    }
282}
283
284impl From<ParseFloatError> for ParseFiniteFloatError {
285    fn from(err: ParseFloatError) -> Self {
286        Self(ParseFiniteFloatErrorKind::Invalid(err))
287    }
288}
289
290impl From<TryFromFloatError> for ParseFiniteFloatError {
291    fn from(err: TryFromFloatError) -> Self {
292        Self(ParseFiniteFloatErrorKind::NonFinite(err))
293    }
294}
295
296impl FromStr for FiniteF64 {
297    type Err = ParseFiniteFloatError;
298
299    fn from_str(s: &str) -> Result<Self, Self::Err> {
300        Ok(s.parse::<f64>()?.try_into()?)
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307
308    #[test]
309    fn test_new() {
310        assert_eq!(FiniteF64::new(0.0), Some(FiniteF64(0.0)));
311        assert_eq!(FiniteF64::new(1.0), Some(FiniteF64(1.0)));
312        assert_eq!(FiniteF64::new(-1.0), Some(FiniteF64(-1.0)));
313        assert_eq!(FiniteF64::new(f64::MIN), Some(FiniteF64(f64::MIN)));
314        assert_eq!(FiniteF64::new(f64::MAX), Some(FiniteF64(f64::MAX)));
315        assert_eq!(FiniteF64::new(f64::NAN), None);
316        assert_eq!(FiniteF64::new(f64::INFINITY), None);
317        assert_eq!(FiniteF64::new(f64::NEG_INFINITY), None);
318    }
319
320    #[test]
321    fn test_arithmetics() {
322        assert_eq!(FiniteF64(1.0) + FiniteF64(1.0), Some(FiniteF64(2.0)));
323        assert_eq!(FiniteF64(f64::MAX) + FiniteF64(f64::MAX), None);
324        assert_eq!(FiniteF64(f64::MIN) + FiniteF64(f64::MIN), None);
325
326        assert_eq!(FiniteF64(1.0) - FiniteF64(1.0), Some(FiniteF64(0.0)));
327        assert_eq!(
328            FiniteF64(f64::MAX) - FiniteF64(f64::MAX),
329            Some(FiniteF64(0.0))
330        );
331        assert_eq!(
332            FiniteF64(f64::MIN) - FiniteF64(f64::MIN),
333            Some(FiniteF64(0.0))
334        );
335
336        assert_eq!(FiniteF64(2.0) * FiniteF64(2.0), Some(FiniteF64(4.0)));
337        assert_eq!(FiniteF64(f64::MAX) * FiniteF64(f64::MAX), None);
338        assert_eq!(FiniteF64(f64::MIN) * FiniteF64(f64::MIN), None);
339
340        assert_eq!(FiniteF64(2.0) / FiniteF64(2.0), Some(FiniteF64(1.0)));
341        assert_eq!(FiniteF64(2.0) / FiniteF64(0.0), None); // Infinity
342        assert_eq!(FiniteF64(-2.0) / FiniteF64(0.0), None); // -Infinity
343        assert_eq!(FiniteF64(0.0) / FiniteF64(0.0), None); // NaN
344    }
345
346    #[test]
347    fn test_saturating_add() {
348        assert_eq!(
349            FiniteF64(1.0).saturating_add(FiniteF64(1.0)),
350            FiniteF64(2.0)
351        );
352        assert_eq!(
353            FiniteF64(f64::MAX).saturating_add(FiniteF64(1.0)),
354            FiniteF64(f64::MAX)
355        );
356        assert_eq!(
357            FiniteF64(f64::MIN).saturating_add(FiniteF64(-1.0)),
358            FiniteF64(f64::MIN)
359        );
360    }
361
362    #[test]
363    fn test_saturating_sub() {
364        assert_eq!(
365            FiniteF64(1.0).saturating_sub(FiniteF64(1.0)),
366            FiniteF64(0.0)
367        );
368        assert_eq!(
369            FiniteF64(f64::MAX).saturating_sub(FiniteF64(-1.0)),
370            FiniteF64(f64::MAX)
371        );
372        assert_eq!(
373            FiniteF64(f64::MIN).saturating_sub(FiniteF64(1.0)),
374            FiniteF64(f64::MIN)
375        );
376    }
377
378    #[test]
379    fn test_saturating_mul() {
380        assert_eq!(
381            FiniteF64::from(2).saturating_mul(FiniteF64::from(2)),
382            FiniteF64::from(4)
383        );
384        assert_eq!(
385            FiniteF64(f64::MAX).saturating_mul(FiniteF64::from(2)),
386            FiniteF64(f64::MAX)
387        );
388        assert_eq!(
389            FiniteF64(f64::MIN).saturating_mul(FiniteF64::from(2)),
390            FiniteF64(f64::MIN)
391        );
392    }
393
394    #[test]
395    fn teste_parse() {
396        assert_eq!("0".parse::<FiniteF64>().unwrap(), FiniteF64(0.0));
397        assert_eq!("0.0".parse::<FiniteF64>().unwrap(), FiniteF64(0.0));
398
399        assert!("bla".parse::<FiniteF64>().is_err());
400        assert!("inf".parse::<FiniteF64>().is_err());
401        assert!("-inf".parse::<FiniteF64>().is_err());
402        assert!("NaN".parse::<FiniteF64>().is_err());
403    }
404}