Skip to main content

use_number/
number.rs

1//! Raw-number classification helpers.
2
3use core::num::FpCategory;
4
5/// Broad categories for raw `f64` values.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum NumberCategory {
8    /// The value is not a number.
9    Nan,
10    /// The value is positive or negative infinity.
11    Infinite,
12    /// The value is positive or negative zero.
13    Zero,
14    /// The value is non-zero but below the normal minimum magnitude.
15    Subnormal,
16    /// The value is a finite normal floating-point number.
17    Normal,
18}
19
20/// Sign classes for raw numeric values that are not `NaN`.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum NumberSign {
23    /// The value is below zero.
24    Negative,
25    /// The value is equal to zero, including `-0.0`.
26    Zero,
27    /// The value is above zero.
28    Positive,
29}
30
31/// Classifies a raw `f64` by floating-point category.
32#[must_use]
33pub const fn classify_number(value: f64) -> NumberCategory {
34    match value.classify() {
35        FpCategory::Nan => NumberCategory::Nan,
36        FpCategory::Infinite => NumberCategory::Infinite,
37        FpCategory::Zero => NumberCategory::Zero,
38        FpCategory::Subnormal => NumberCategory::Subnormal,
39        FpCategory::Normal => NumberCategory::Normal,
40    }
41}
42
43/// Classifies the sign of a raw `f64` while keeping `NaN` explicit.
44#[must_use]
45pub const fn classify_number_sign(value: f64) -> Option<NumberSign> {
46    if value.is_nan() {
47        None
48    } else if matches!(value.classify(), FpCategory::Zero) {
49        Some(NumberSign::Zero)
50    } else if value.is_sign_negative() {
51        Some(NumberSign::Negative)
52    } else {
53        Some(NumberSign::Positive)
54    }
55}
56
57/// Returns whether a raw `f64` is finite.
58#[must_use]
59pub const fn is_finite_number(value: f64) -> bool {
60    value.is_finite()
61}
62
63#[cfg(test)]
64mod tests {
65    use super::{
66        NumberCategory, NumberSign, classify_number, classify_number_sign, is_finite_number,
67    };
68
69    #[test]
70    fn classifies_floating_point_categories() {
71        assert_eq!(classify_number(f64::NAN), NumberCategory::Nan);
72        assert_eq!(classify_number(f64::INFINITY), NumberCategory::Infinite);
73        assert_eq!(classify_number(0.0), NumberCategory::Zero);
74        assert_eq!(
75            classify_number(f64::from_bits(1)),
76            NumberCategory::Subnormal
77        );
78        assert_eq!(classify_number(-4.0), NumberCategory::Normal);
79    }
80
81    #[test]
82    fn classifies_signs_without_hiding_nan() {
83        assert_eq!(classify_number_sign(-12.0), Some(NumberSign::Negative));
84        assert_eq!(classify_number_sign(-0.0), Some(NumberSign::Zero));
85        assert_eq!(classify_number_sign(18.0), Some(NumberSign::Positive));
86        assert_eq!(classify_number_sign(f64::NAN), None);
87    }
88
89    #[test]
90    fn reports_finiteness() {
91        assert!(is_finite_number(1.25));
92        assert!(!is_finite_number(f64::NEG_INFINITY));
93        assert!(!is_finite_number(f64::NAN));
94    }
95}