lifering/
lib.rs

1use num::Float;
2use std::fmt::Debug;
3
4/// A mantissa-exponent-sign triple that represents a floating point number.
5#[derive(Clone, Eq, Hash, PartialEq)]
6pub struct FloatingPointComponents(u64, i16, i8);
7
8/// Error type that indicates a `NaN` was used to attempt to create [`FloatingPointComponents`].
9#[derive(Debug)]
10pub struct NanError;
11
12#[doc(hidden)]
13pub struct FloatWrap<F: Float>(F);
14
15impl FloatingPointComponents {
16    /// Creates new [`FloatingPointComponents`] from a [`Float`].
17    #[inline]
18    pub fn new<F: Float>(num: F) -> Result<Self, NanError> {
19        Self::try_from(FloatWrap(num))
20    }
21}
22
23impl FloatingPointComponents {
24    /// Returns the [`f32`] value of these [`FloatingPointComponents`].
25    #[inline]
26    pub fn as_f32(&self) -> f32 {
27        let sign_f = self.2 as f32;
28        let mantissa_f = self.0 as f32;
29        let exponent_f = (2 as f32).powf(self.1 as f32);
30
31        sign_f * mantissa_f * exponent_f
32    }
33
34    /// Returns the [`f64`] value of these [`FloatingPointComponents`].
35    #[inline]
36    pub fn as_f64(&self) -> f64 {
37        let sign_f = self.2 as f64;
38        let mantissa_f = self.0 as f64;
39        let exponent_f = (2 as f64).powf(self.1 as f64);
40
41        sign_f * mantissa_f * exponent_f
42    }
43}
44
45impl std::fmt::Debug for FloatingPointComponents {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        f.debug_struct("Float")
48            .field("sign", &self.2)
49            .field("mantissa", &self.0)
50            .field("exponent", &(2 as f64).powf(self.1 as f64))
51            .finish()
52            .and(write!(f, " ({})", self.as_f64()))
53    }
54}
55
56impl<F: Float> TryFrom<FloatWrap<F>> for FloatingPointComponents {
57    type Error = NanError;
58
59    fn try_from(value: FloatWrap<F>) -> Result<Self, Self::Error> {
60        let value = value.0;
61
62        if value.is_nan() {
63            return Err(NanError);
64        }
65
66        let (mantissa, exponent, sign) = value.integer_decode();
67
68        Ok(Self(mantissa, exponent, sign))
69    }
70}
71
72impl Into<f64> for &FloatingPointComponents {
73    fn into(self) -> f64 {
74        let sign_f = self.2 as f64;
75        let mantissa_f = self.0 as f64;
76        let exponent_f = (2 as f64).powf(self.1 as f64);
77
78        sign_f * mantissa_f * exponent_f
79    }
80}
81
82impl Into<f32> for &FloatingPointComponents {
83    fn into(self) -> f32 {
84        let sign_f = self.2 as f32;
85        let mantissa_f = self.0 as f32;
86        let exponent_f = (2 as f32).powf(self.1 as f32);
87
88        sign_f * mantissa_f * exponent_f
89    }
90}
91
92impl Into<f64> for FloatingPointComponents {
93    fn into(self) -> f64 {
94        let sign_f = self.2 as f64;
95        let mantissa_f = self.0 as f64;
96        let exponent_f = (2 as f64).powf(self.1 as f64);
97
98        sign_f * mantissa_f * exponent_f
99    }
100}
101
102impl Into<f32> for FloatingPointComponents {
103    fn into(self) -> f32 {
104        let sign_f = self.2 as f32;
105        let mantissa_f = self.0 as f32;
106        let exponent_f = (2 as f32).powf(self.1 as f32);
107
108        sign_f * mantissa_f * exponent_f
109    }
110}
111
112/// Easier way to make [`FloatingPointComponents`] from a [`Float`].
113///
114/// # Panics
115/// This macro unwraps internally, if you would like to use custom handling for
116/// the potential failure, you should manually create [`FloatingPointComponents`].
117#[macro_export]
118macro_rules! lifering {
119    ($float:expr) => {
120        FloatingPointComponents::new($float).unwrap()
121    };
122}