Skip to content

Commit

Permalink
Merge #279
Browse files Browse the repository at this point in the history
279: Add FloatCore and Float::is_subnormal r=cuviper a=cuviper

This is a rebase of #265, adding a default impl and an autocfg check.

Closes #264.

Co-authored-by: Fredrick Brennan <copypaste@kittens.ph>
Co-authored-by: Josh Stone <cuviper@gmail.com>
  • Loading branch information
3 people committed Jul 20, 2023
2 parents 85edb5e + 58b46b1 commit c263032
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 5 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
1.38.0, # has_div_euclid
1.44.0, # has_to_int_unchecked
1.46.0, # has_leading_trailing_ones
1.53.0, # has_is_subnormal
stable,
beta,
nightly,
Expand Down
1 change: 1 addition & 0 deletions bors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ status = [
"Test (1.38.0)",
"Test (1.44.0)",
"Test (1.46.0)",
"Test (1.53.0)",
"Test (stable)",
"Test (beta)",
"Test (nightly)",
Expand Down
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn main() {
if env::var_os("CARGO_FEATURE_STD").is_some() {
ac.emit_expression_cfg("1f64.copysign(-1f64)", "has_copysign");
}
ac.emit_expression_cfg("1f64.is_subnormal()", "has_is_subnormal");

autocfg::rerun_path("build.rs");
}
2 changes: 1 addition & 1 deletion ci/rustup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
set -ex

ci=$(dirname $0)
for version in 1.31.0 1.35.0 1.37.0 1.38.0 1.44.0 1.46.0 stable beta nightly; do
for version in 1.31.0 1.35.0 1.37.0 1.38.0 1.44.0 1.46.0 1.53.0 stable beta nightly; do
rustup run "$version" "$ci/test_full.sh"
done
85 changes: 81 additions & 4 deletions src/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,32 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
self.classify() == FpCategory::Normal
}

/// Returns `true` if the number is [subnormal].
///
/// ```
/// use num_traits::float::FloatCore;
/// use std::f64;
///
/// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308_f64
/// let max = f64::MAX;
/// let lower_than_min = 1.0e-308_f64;
/// let zero = 0.0_f64;
///
/// assert!(!min.is_subnormal());
/// assert!(!max.is_subnormal());
///
/// assert!(!zero.is_subnormal());
/// assert!(!f64::NAN.is_subnormal());
/// assert!(!f64::INFINITY.is_subnormal());
/// // Values between `0` and `min` are Subnormal.
/// assert!(lower_than_min.is_subnormal());
/// ```
/// [subnormal]: https://en.wikipedia.org/wiki/Subnormal_number
#[inline]
fn is_subnormal(self) -> bool {
self.classify() == FpCategory::Subnormal
}

/// Returns the floating point category of the number. If only one property
/// is going to be tested, it is generally faster to use the specific
/// predicate instead.
Expand Down Expand Up @@ -918,6 +944,11 @@ impl FloatCore for f64 {
Self::to_radians(self) -> Self;
}

#[cfg(has_is_subnormal)]
forward! {
Self::is_subnormal(self) -> bool;
}

#[cfg(all(not(feature = "std"), feature = "libm"))]
forward! {
libm::floor as floor(self) -> Self;
Expand Down Expand Up @@ -1123,9 +1154,35 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
/// // Values between `0` and `min` are Subnormal.
/// assert!(!lower_than_min.is_normal());
/// ```
/// [subnormal]: http://en.wikipedia.org/wiki/Denormal_number
/// [subnormal]: http://en.wikipedia.org/wiki/Subnormal_number
fn is_normal(self) -> bool;

/// Returns `true` if the number is [subnormal].
///
/// ```
/// use num_traits::Float;
/// use std::f64;
///
/// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308_f64
/// let max = f64::MAX;
/// let lower_than_min = 1.0e-308_f64;
/// let zero = 0.0_f64;
///
/// assert!(!min.is_subnormal());
/// assert!(!max.is_subnormal());
///
/// assert!(!zero.is_subnormal());
/// assert!(!f64::NAN.is_subnormal());
/// assert!(!f64::INFINITY.is_subnormal());
/// // Values between `0` and `min` are Subnormal.
/// assert!(lower_than_min.is_subnormal());
/// ```
/// [subnormal]: https://en.wikipedia.org/wiki/Subnormal_number
#[inline]
fn is_subnormal(self) -> bool {
self.classify() == FpCategory::Subnormal
}

/// Returns the floating point category of the number. If only one property
/// is going to be tested, it is generally faster to use the specific
/// predicate instead.
Expand Down Expand Up @@ -1959,9 +2016,13 @@ macro_rules! float_impl_std {
}

#[cfg(has_copysign)]
#[inline]
fn copysign(self, sign: Self) -> Self {
Self::copysign(self, sign)
forward! {
Self::copysign(self, sign: Self) -> Self;
}

#[cfg(has_is_subnormal)]
forward! {
Self::is_subnormal(self) -> bool;
}
}
};
Expand Down Expand Up @@ -2318,6 +2379,7 @@ mod tests {
assert!(p.is_sign_positive());
assert!(n.is_sign_negative());
assert!(nan.is_nan());
assert!(!nan.is_subnormal());

assert_eq!(p, p.copysign(p));
assert_eq!(p.neg(), p.copysign(n));
Expand All @@ -2328,4 +2390,19 @@ mod tests {
assert!(nan.copysign(p).is_sign_positive());
assert!(nan.copysign(n).is_sign_negative());
}

#[cfg(any(feature = "std", feature = "libm"))]
fn test_subnormal<F: crate::float::Float + ::core::fmt::Debug>() {
let min_positive = F::min_positive_value();
let lower_than_min = min_positive / F::from(2.0f32).unwrap();
assert!(!min_positive.is_subnormal());
assert!(lower_than_min.is_subnormal());
}

#[test]
#[cfg(any(feature = "std", feature = "libm"))]
fn subnormal() {
test_subnormal::<f64>();
test_subnormal::<f32>();
}
}

0 comments on commit c263032

Please sign in to comment.