use core::num::FpCategory;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NumberCategory {
Nan,
Infinite,
Zero,
Subnormal,
Normal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NumberSign {
Negative,
Zero,
Positive,
}
#[must_use]
pub const fn classify_number(value: f64) -> NumberCategory {
match value.classify() {
FpCategory::Nan => NumberCategory::Nan,
FpCategory::Infinite => NumberCategory::Infinite,
FpCategory::Zero => NumberCategory::Zero,
FpCategory::Subnormal => NumberCategory::Subnormal,
FpCategory::Normal => NumberCategory::Normal,
}
}
#[must_use]
pub const fn classify_number_sign(value: f64) -> Option<NumberSign> {
if value.is_nan() {
None
} else if matches!(value.classify(), FpCategory::Zero) {
Some(NumberSign::Zero)
} else if value.is_sign_negative() {
Some(NumberSign::Negative)
} else {
Some(NumberSign::Positive)
}
}
#[must_use]
pub const fn is_finite_number(value: f64) -> bool {
value.is_finite()
}
#[cfg(test)]
mod tests {
use super::{
NumberCategory, NumberSign, classify_number, classify_number_sign, is_finite_number,
};
#[test]
fn classifies_floating_point_categories() {
assert_eq!(classify_number(f64::NAN), NumberCategory::Nan);
assert_eq!(classify_number(f64::INFINITY), NumberCategory::Infinite);
assert_eq!(classify_number(0.0), NumberCategory::Zero);
assert_eq!(
classify_number(f64::from_bits(1)),
NumberCategory::Subnormal
);
assert_eq!(classify_number(-4.0), NumberCategory::Normal);
}
#[test]
fn classifies_signs_without_hiding_nan() {
assert_eq!(classify_number_sign(-12.0), Some(NumberSign::Negative));
assert_eq!(classify_number_sign(-0.0), Some(NumberSign::Zero));
assert_eq!(classify_number_sign(18.0), Some(NumberSign::Positive));
assert_eq!(classify_number_sign(f64::NAN), None);
}
#[test]
fn reports_finiteness() {
assert!(is_finite_number(1.25));
assert!(!is_finite_number(f64::NEG_INFINITY));
assert!(!is_finite_number(f64::NAN));
}
}