diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 261f2440..95eb8264 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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, diff --git a/bors.toml b/bors.toml index fa15ce17..7314c271 100644 --- a/bors.toml +++ b/bors.toml @@ -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)", diff --git a/build.rs b/build.rs index 639d0582..b0aa7d65 100644 --- a/build.rs +++ b/build.rs @@ -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"); } diff --git a/ci/rustup.sh b/ci/rustup.sh index fb3da186..56a8df10 100755 --- a/ci/rustup.sh +++ b/ci/rustup.sh @@ -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 diff --git a/src/float.rs b/src/float.rs index 33cbf9cf..26a6d63b 100644 --- a/src/float.rs +++ b/src/float.rs @@ -241,6 +241,32 @@ pub trait FloatCore: Num + NumCast + Neg + 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. @@ -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; @@ -1123,9 +1154,35 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { /// // 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. @@ -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; } } }; @@ -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)); @@ -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() { + 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::(); + test_subnormal::(); + } }