lilliput_float/
classify.rs

1use std::num::FpCategory;
2
3use crate::bits::FpToBits;
4use crate::floats::{F16, F24, F32, F40, F48, F56, F64, F8};
5use crate::repr::FpRepr;
6use crate::PackedFloat;
7
8pub trait FpClassify: Sized {
9    fn classify(&self) -> FpCategory;
10
11    fn is_zero(&self) -> bool {
12        matches!(self.classify(), FpCategory::Zero)
13    }
14
15    fn is_nan(&self) -> bool {
16        matches!(self.classify(), FpCategory::Nan)
17    }
18
19    fn is_infinite(&self) -> bool {
20        matches!(self.classify(), FpCategory::Infinite)
21    }
22
23    fn is_subnormal(&self) -> bool {
24        matches!(self.classify(), FpCategory::Subnormal)
25    }
26
27    fn is_normal(&self) -> bool {
28        matches!(self.classify(), FpCategory::Normal)
29    }
30}
31
32macro_rules! impl_float_classify {
33    ($t:ty) => {
34        impl FpClassify for $t {
35            fn classify(&self) -> FpCategory {
36                let bits = self.to_bits();
37                let exponent_bits = bits & Self::EXPONENT_MASK;
38                let significand_bits = bits & Self::SIGNIFICAND_MASK;
39
40                match (exponent_bits, significand_bits) {
41                    (Self::EXPONENT_MASK, 0) => FpCategory::Infinite,
42                    (Self::EXPONENT_MASK, _) => FpCategory::Nan,
43                    (0, 0) => FpCategory::Zero,
44                    (0, _) => FpCategory::Subnormal,
45                    _ => FpCategory::Normal,
46                }
47            }
48        }
49    };
50}
51
52impl_float_classify!(F8);
53impl_float_classify!(F16);
54impl_float_classify!(F24);
55impl_float_classify!(F32);
56impl_float_classify!(F40);
57impl_float_classify!(F48);
58impl_float_classify!(F56);
59impl_float_classify!(F64);
60
61impl FpClassify for PackedFloat {
62    fn classify(&self) -> FpCategory {
63        match self {
64            Self::F8(value) => value.classify(),
65            Self::F16(value) => value.classify(),
66            Self::F24(value) => value.classify(),
67            Self::F32(value) => value.classify(),
68            Self::F40(value) => value.classify(),
69            Self::F48(value) => value.classify(),
70            Self::F56(value) => value.classify(),
71            Self::F64(value) => value.classify(),
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use proptest::prelude::*;
79
80    use super::*;
81
82    proptest! {
83        #[test]
84        fn f32_matches_native_behavior(native in f32::arbitrary()) {
85            let subject = F32::from(native);
86            let actual = subject.classify();
87            let expected = native.classify();
88            prop_assert_eq!(actual, expected);
89        }
90
91        #[test]
92        fn f64_matches_native_behavior(native in f64::arbitrary()) {
93            let subject = F64::from(native);
94            let actual = subject.classify();
95            let expected = native.classify();
96            prop_assert_eq!(actual, expected);
97        }
98    }
99}