From 3b9fe77bfcd4c800fab8afcdcc82d31facdc515b Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 4 Jun 2017 21:44:57 +0300 Subject: [PATCH] Fix NaN handling in is_sign_negative/positive See #42425 --- src/libcore/num/f32.rs | 19 +++++++++++++------ src/libcore/num/f64.rs | 17 +++++++++++------ src/libstd/f32.rs | 24 ++++++++---------------- src/libstd/f64.rs | 26 ++++++++------------------ 4 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 91ca213e96e0d..aca37221f094f 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -205,18 +205,25 @@ impl Float for f32 { } } - /// Returns `true` if `self` is positive, including `+0.0` and - /// `Float::infinity()`. + /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. #[inline] fn is_sign_positive(self) -> bool { - self > 0.0 || (1.0 / self) == INFINITY + !self.is_sign_negative() } - /// Returns `true` if `self` is negative, including `-0.0` and - /// `Float::neg_infinity()`. + /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. #[inline] fn is_sign_negative(self) -> bool { - self < 0.0 || (1.0 / self) == NEG_INFINITY + // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus + // applies to zeros and NaNs as well. + #[repr(C)] + union F32Bytes { + f: f32, + b: u32 + } + unsafe { F32Bytes { f: self }.b & 0x8000_0000 != 0 } } /// Returns the reciprocal (multiplicative inverse) of the number. diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 7d6d6cef04977..ebe5daa67a77f 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -205,18 +205,23 @@ impl Float for f64 { } } - /// Returns `true` if `self` is positive, including `+0.0` and - /// `Float::infinity()`. + /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. #[inline] fn is_sign_positive(self) -> bool { - self > 0.0 || (1.0 / self) == INFINITY + !self.is_sign_negative() } - /// Returns `true` if `self` is negative, including `-0.0` and - /// `Float::neg_infinity()`. + /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. #[inline] fn is_sign_negative(self) -> bool { - self < 0.0 || (1.0 / self) == NEG_INFINITY + #[repr(C)] + union F64Bytes { + f: f64, + b: u64 + } + unsafe { F64Bytes { f: self }.b & 0x8000_0000_0000_0000 != 0 } } /// Returns the reciprocal (multiplicative inverse) of the number. diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 4abad7e24f810..2f5a7c72411c3 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -365,39 +365,29 @@ impl f32 { #[inline] pub fn signum(self) -> f32 { num::Float::signum(self) } - /// Returns `true` if `self`'s sign bit is positive, including - /// `+0.0` and `INFINITY`. + /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. /// /// ``` - /// use std::f32; - /// - /// let nan = f32::NAN; /// let f = 7.0_f32; /// let g = -7.0_f32; /// /// assert!(f.is_sign_positive()); /// assert!(!g.is_sign_positive()); - /// // Requires both tests to determine if is `NaN` - /// assert!(!nan.is_sign_positive() && !nan.is_sign_negative()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_sign_positive(self) -> bool { num::Float::is_sign_positive(self) } - /// Returns `true` if `self`'s sign is negative, including `-0.0` - /// and `NEG_INFINITY`. + /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. /// /// ``` - /// use std::f32; - /// - /// let nan = f32::NAN; /// let f = 7.0f32; /// let g = -7.0f32; /// /// assert!(!f.is_sign_negative()); /// assert!(g.is_sign_negative()); - /// // Requires both tests to determine if is `NaN`. - /// assert!(!nan.is_sign_positive() && !nan.is_sign_negative()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1186,7 +1176,7 @@ mod tests { assert!(!nan.is_infinite()); assert!(!nan.is_finite()); assert!(!nan.is_normal()); - assert!(!nan.is_sign_positive()); + assert!(nan.is_sign_positive()); assert!(!nan.is_sign_negative()); assert_eq!(Fp::Nan, nan.classify()); } @@ -1430,7 +1420,8 @@ mod tests { assert!(!(-1f32).is_sign_positive()); assert!(!NEG_INFINITY.is_sign_positive()); assert!(!(1f32/NEG_INFINITY).is_sign_positive()); - assert!(!NAN.is_sign_positive()); + assert!(NAN.is_sign_positive()); + assert!(!(-NAN).is_sign_positive()); } #[test] @@ -1443,6 +1434,7 @@ mod tests { assert!(NEG_INFINITY.is_sign_negative()); assert!((1f32/NEG_INFINITY).is_sign_negative()); assert!(!NAN.is_sign_negative()); + assert!((-NAN).is_sign_negative()); } #[test] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 82e3903eec7b1..3513546fb64c4 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -303,21 +303,15 @@ impl f64 { #[inline] pub fn signum(self) -> f64 { num::Float::signum(self) } - /// Returns `true` if `self`'s sign bit is positive, including - /// `+0.0` and `INFINITY`. + /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. /// /// ``` - /// use std::f64; - /// - /// let nan: f64 = f64::NAN; - /// /// let f = 7.0_f64; /// let g = -7.0_f64; /// /// assert!(f.is_sign_positive()); /// assert!(!g.is_sign_positive()); - /// // Requires both tests to determine if is `NaN` - /// assert!(!nan.is_sign_positive() && !nan.is_sign_negative()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -328,21 +322,15 @@ impl f64 { #[inline] pub fn is_positive(self) -> bool { num::Float::is_sign_positive(self) } - /// Returns `true` if `self`'s sign is negative, including `-0.0` - /// and `NEG_INFINITY`. + /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. /// /// ``` - /// use std::f64; - /// - /// let nan = f64::NAN; - /// /// let f = 7.0_f64; /// let g = -7.0_f64; /// /// assert!(!f.is_sign_negative()); /// assert!(g.is_sign_negative()); - /// // Requires both tests to determine if is `NaN`. - /// assert!(!nan.is_sign_positive() && !nan.is_sign_negative()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1103,7 +1091,7 @@ mod tests { assert!(!nan.is_infinite()); assert!(!nan.is_finite()); assert!(!nan.is_normal()); - assert!(!nan.is_sign_positive()); + assert!(nan.is_sign_positive()); assert!(!nan.is_sign_negative()); assert_eq!(Fp::Nan, nan.classify()); } @@ -1346,7 +1334,8 @@ mod tests { assert!(!(-1f64).is_sign_positive()); assert!(!NEG_INFINITY.is_sign_positive()); assert!(!(1f64/NEG_INFINITY).is_sign_positive()); - assert!(!NAN.is_sign_positive()); + assert!(NAN.is_sign_positive()); + assert!(!(-NAN).is_sign_positive()); } #[test] @@ -1359,6 +1348,7 @@ mod tests { assert!(NEG_INFINITY.is_sign_negative()); assert!((1f64/NEG_INFINITY).is_sign_negative()); assert!(!NAN.is_sign_negative()); + assert!((-NAN).is_sign_negative()); } #[test]