From f202a0ba60376e3f0ad24eeedb8b42508e472014 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Tue, 7 Sep 2021 18:46:49 +0200 Subject: [PATCH 1/8] Added next_up and next_down for f32/f64. --- library/core/src/num/f32.rs | 98 ++++++++++++++++++++++++++++++++++++ library/core/src/num/f64.rs | 98 ++++++++++++++++++++++++++++++++++++ library/std/src/f32/tests.rs | 63 +++++++++++++++++++++++ library/std/src/f64/tests.rs | 63 +++++++++++++++++++++++ library/std/src/lib.rs | 1 + 5 files changed, 323 insertions(+) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 83a922ae34891..c5a23d4927d55 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -620,6 +620,104 @@ impl f32 { self.to_bits() & 0x8000_0000 != 0 } + /// Returns the least number greater than `self`. + /// + /// Let `TINY` be the smallest representable positive `f32`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`NEG_INFINITY`], this returns [`MIN`]; + /// - if `self` is `-TINY`, this returns -0.0; + /// - if `self` is -0.0 or +0.0, this returns `TINY`; + /// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`]; + /// - otherwise the unique least value greater than `self` is returned. + /// + /// The identity `x.next_up() == -(-x).next_down()` holds for all `x`. When `x` + /// is finite `x == x.next_up().next_down()` also holds. + /// + /// ```rust + /// #![feature(float_next_up_down)] + /// // f32::EPSILON is the difference between 1.0 and the next number up. + /// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + /// // But not for most numbers. + /// assert!(0.1f32.next_up() < 0.1 + f32::EPSILON); + /// assert_eq!(16777216f32.next_up(), 16777218.0); + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[unstable(feature = "float_next_up_down", issue = "none")] + pub const fn next_up(self) -> Self { + // We must use strictly integer arithmetic to prevent denormals from + // flushing to zero after an arithmetic operation on some platforms. + const TINY_BITS: u32 = 0x1; // Smallest positive f32. + const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; + + let bits = self.to_bits(); + if self.is_nan() || bits == Self::INFINITY.to_bits() { + return self; + } + + let abs = bits & CLEAR_SIGN_MASK; + let next_bits = if abs == 0 { + TINY_BITS + } else if bits == abs { + bits + 1 + } else { + bits - 1 + }; + Self::from_bits(next_bits) + } + + /// Returns the greatest number less than `self`. + /// + /// Let `TINY` be the smallest representable positive `f32`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`INFINITY`], this returns [`MAX`]; + /// - if `self` is `TINY`, this returns 0.0; + /// - if `self` is -0.0 or +0.0, this returns `-TINY`; + /// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`]; + /// - otherwise the unique greatest value less than `self` is returned. + /// + /// The identity `x.next_down() == -(-x).next_up()` holds for all `x`. When `x` + /// is finite `x == x.next_down().next_up()` also holds. + /// + /// ```rust + /// #![feature(float_next_up_down)] + /// let x = 1.0f32; + /// // Clamp value into range [0, 1). + /// let clamped = x.clamp(0.0, 1.0f32.next_down()); + /// assert!(clamped < 1.0); + /// assert_eq!(clamped.next_up(), 1.0); + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[unstable(feature = "float_next_up_down", issue = "none")] + pub const fn next_down(self) -> Self { + // We must use strictly integer arithmetic to prevent denormals from + // flushing to zero after an arithmetic operation on some platforms. + const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32. + const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; + + let bits = self.to_bits(); + if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { + return self; + } + + let abs = bits & CLEAR_SIGN_MASK; + let next_bits = if abs == 0 { + NEG_TINY_BITS + } else if bits == abs { + bits - 1 + } else { + bits + 1 + }; + Self::from_bits(next_bits) + } + /// Takes the reciprocal (inverse) of a number, `1/x`. /// /// ``` diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 4267260eea38c..db09c8bb2430f 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -635,6 +635,104 @@ impl f64 { self.is_sign_negative() } + /// Returns the least number greater than `self`. + /// + /// Let `TINY` be the smallest representable positive `f64`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`NEG_INFINITY`], this returns [`MIN`]; + /// - if `self` is `-TINY`, this returns -0.0; + /// - if `self` is -0.0 or +0.0, this returns `TINY`; + /// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`]; + /// - otherwise the unique least value greater than `self` is returned. + /// + /// The identity `x.next_up() == -(-x).next_down()` holds for all `x`. When `x` + /// is finite `x == x.next_up().next_down()` also holds. + /// + /// ```rust + /// #![feature(float_next_up_down)] + /// // f64::EPSILON is the difference between 1.0 and the next number up. + /// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + /// // But not for most numbers. + /// assert!(0.1f64.next_up() < 0.1 + f64::EPSILON); + /// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0); + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[unstable(feature = "float_next_up_down", issue = "none")] + pub const fn next_up(self) -> Self { + // We must use strictly integer arithmetic to prevent denormals from + // flushing to zero after an arithmetic operation on some platforms. + const TINY_BITS: u64 = 0x1; // Smallest positive f64. + const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff; + + let bits = self.to_bits(); + if self.is_nan() || bits == Self::INFINITY.to_bits() { + return self; + } + + let abs = bits & CLEAR_SIGN_MASK; + let next_bits = if abs == 0 { + TINY_BITS + } else if bits == abs { + bits + 1 + } else { + bits - 1 + }; + Self::from_bits(next_bits) + } + + /// Returns the greatest number less than `self`. + /// + /// Let `TINY` be the smallest representable positive `f64`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`INFINITY`], this returns [`MAX`]; + /// - if `self` is `TINY`, this returns 0.0; + /// - if `self` is -0.0 or +0.0, this returns `-TINY`; + /// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`]; + /// - otherwise the unique greatest value less than `self` is returned. + /// + /// The identity `x.next_down() == -(-x).next_up()` holds for all `x`. When `x` + /// is finite `x == x.next_down().next_up()` also holds. + /// + /// ```rust + /// #![feature(float_next_up_down)] + /// let x = 1.0f64; + /// // Clamp value into range [0, 1). + /// let clamped = x.clamp(0.0, 1.0f64.next_down()); + /// assert!(clamped < 1.0); + /// assert_eq!(clamped.next_up(), 1.0); + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[unstable(feature = "float_next_up_down", issue = "none")] + pub const fn next_down(self) -> Self { + // We must use strictly integer arithmetic to prevent denormals from + // flushing to zero after an arithmetic operation on some platforms. + const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64. + const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff; + + let bits = self.to_bits(); + if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { + return self; + } + + let abs = bits & CLEAR_SIGN_MASK; + let next_bits = if abs == 0 { + NEG_TINY_BITS + } else if bits == abs { + bits - 1 + } else { + bits + 1 + }; + Self::from_bits(next_bits) + } + /// Takes the reciprocal (inverse) of a number, `1/x`. /// /// ``` diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 0d4b865f3392a..bc2adfc2f96dd 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -287,6 +287,69 @@ fn test_is_sign_negative() { assert!((-f32::NAN).is_sign_negative()); } +#[test] +fn test_next_up() { + let tiny = f32::from_bits(1); + let tiny_up = f32::from_bits(2); + let max_down = f32::from_bits(0x7f7f_fffe); + let largest_subnormal = f32::from_bits(0x007f_ffff); + let smallest_normal = f32::from_bits(0x0080_0000); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN.to_bits(); + let nan1 = f32::NAN.to_bits() ^ 0x002a_aaaa; + let nan2 = f32::NAN.to_bits() ^ 0x0055_5555; + assert_eq!(f32::from_bits(nan0).next_up().to_bits(), nan0); + assert_eq!(f32::from_bits(nan1).next_up().to_bits(), nan1); + assert_eq!(f32::from_bits(nan2).next_up().to_bits(), nan2); + + assert_eq!(f32::NEG_INFINITY.next_up(), f32::MIN); + assert_eq!(f32::MIN.next_up(), -max_down); + assert_eq!((-1.0 - f32::EPSILON).next_up(), -1.0); + assert_eq!((-smallest_normal).next_up(), -largest_subnormal); + assert_eq!((-tiny_up).next_up(), -tiny); + assert_eq!((-tiny).next_up().to_bits(), (-0.0f32).to_bits()); + assert_eq!((-0.0f32).next_up(), tiny); + assert_eq!(0.0f32.next_up(), tiny); + assert_eq!(tiny.next_up(), tiny_up); + assert_eq!(largest_subnormal.next_up(), smallest_normal); + assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + assert_eq!(f32::MAX.next_up(), f32::INFINITY); + assert_eq!(f32::INFINITY.next_up(), f32::INFINITY); +} + +#[test] +fn test_next_down() { + let tiny = f32::from_bits(1); + let tiny_up = f32::from_bits(2); + let max_down = f32::from_bits(0x7f7f_fffe); + let largest_subnormal = f32::from_bits(0x007f_ffff); + let smallest_normal = f32::from_bits(0x0080_0000); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN.to_bits(); + let nan1 = f32::NAN.to_bits() ^ 0x002a_aaaa; + let nan2 = f32::NAN.to_bits() ^ 0x0055_5555; + assert_eq!(f32::from_bits(nan0).next_down().to_bits(), nan0); + assert_eq!(f32::from_bits(nan1).next_down().to_bits(), nan1); + assert_eq!(f32::from_bits(nan2).next_down().to_bits(), nan2); + + assert_eq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); + assert_eq!(f32::MIN.next_down(), f32::NEG_INFINITY); + assert_eq!((-max_down).next_down(), f32::MIN); + assert_eq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); + assert_eq!((-largest_subnormal).next_down(), -smallest_normal); + assert_eq!((-tiny).next_down(), -tiny_up); + assert_eq!((-0.0f32).next_down(), -tiny); + assert_eq!((0.0f32).next_down(), -tiny); + assert_eq!(tiny.next_down().to_bits(), 0.0f32.to_bits()); + assert_eq!(tiny_up.next_down(), tiny); + assert_eq!(smallest_normal.next_down(), largest_subnormal); + assert_eq!((1.0 + f32::EPSILON).next_down(), 1.0f32); + assert_eq!(f32::MAX.next_down(), max_down); + assert_eq!(f32::INFINITY.next_down(), f32::MAX); +} + #[test] fn test_mul_add() { let nan: f32 = f32::NAN; diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 5c163cfe90e0b..8e172cf4963d2 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -289,6 +289,69 @@ fn test_is_sign_negative() { assert!((-f64::NAN).is_sign_negative()); } +#[test] +fn test_next_up() { + let tiny = f64::from_bits(1); + let tiny_up = f64::from_bits(2); + let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); + let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); + let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); + + // Check that NaNs roundtrip. + let nan0 = f64::NAN.to_bits(); + let nan1 = f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa; + let nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + assert_eq!(f64::from_bits(nan0).next_up().to_bits(), nan0); + assert_eq!(f64::from_bits(nan1).next_up().to_bits(), nan1); + assert_eq!(f64::from_bits(nan2).next_up().to_bits(), nan2); + + assert_eq!(f64::NEG_INFINITY.next_up(), f64::MIN); + assert_eq!(f64::MIN.next_up(), -max_down); + assert_eq!((-1.0 - f64::EPSILON).next_up(), -1.0); + assert_eq!((-smallest_normal).next_up(), -largest_subnormal); + assert_eq!((-tiny_up).next_up(), -tiny); + assert_eq!((-tiny).next_up().to_bits(), (-0.0f64).to_bits()); + assert_eq!((-0.0f64).next_up(), tiny); + assert_eq!(0.0f64.next_up(), tiny); + assert_eq!(tiny.next_up(), tiny_up); + assert_eq!(largest_subnormal.next_up(), smallest_normal); + assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + assert_eq!(f64::MAX.next_up(), f64::INFINITY); + assert_eq!(f64::INFINITY.next_up(), f64::INFINITY); +} + +#[test] +fn test_next_down() { + let tiny = f64::from_bits(1); + let tiny_up = f64::from_bits(2); + let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); + let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); + let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); + + // Check that NaNs roundtrip. + let nan0 = f64::NAN.to_bits(); + let nan1 = f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa; + let nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + assert_eq!(f64::from_bits(nan0).next_down().to_bits(), nan0); + assert_eq!(f64::from_bits(nan1).next_down().to_bits(), nan1); + assert_eq!(f64::from_bits(nan2).next_down().to_bits(), nan2); + + assert_eq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); + assert_eq!(f64::MIN.next_down(), f64::NEG_INFINITY); + assert_eq!((-max_down).next_down(), f64::MIN); + assert_eq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); + assert_eq!((-largest_subnormal).next_down(), -smallest_normal); + assert_eq!((-tiny).next_down(), -tiny_up); + assert_eq!((-0.0f64).next_down(), -tiny); + assert_eq!((0.0f64).next_down(), -tiny); + assert_eq!(tiny.next_down().to_bits(), 0.0f64.to_bits()); + assert_eq!(tiny_up.next_down(), tiny); + assert_eq!(smallest_normal.next_down(), largest_subnormal); + assert_eq!((1.0 + f64::EPSILON).next_down(), 1.0f64); + assert_eq!(f64::MAX.next_down(), max_down); + assert_eq!(f64::INFINITY.next_down(), f64::MAX); +} + #[test] fn test_mul_add() { let nan: f64 = f64::NAN; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c2243b259538a..1f7a421673ded 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -285,6 +285,7 @@ #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(extend_one)] +#![feature(float_next_up_down)] #![feature(fn_traits)] #![feature(format_args_nl)] #![feature(gen_future)] From 6f543eef359f72bee7797073d417b8857268cb17 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Sat, 30 Oct 2021 09:25:04 +0200 Subject: [PATCH 2/8] Fixed float next_up/down 32-bit x87 float NaN roundtrip test case. --- library/std/src/f32/tests.rs | 24 ++++++++++++------------ library/std/src/f64/tests.rs | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index bc2adfc2f96dd..938fd34d84f62 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -296,12 +296,12 @@ fn test_next_up() { let smallest_normal = f32::from_bits(0x0080_0000); // Check that NaNs roundtrip. - let nan0 = f32::NAN.to_bits(); - let nan1 = f32::NAN.to_bits() ^ 0x002a_aaaa; - let nan2 = f32::NAN.to_bits() ^ 0x0055_5555; - assert_eq!(f32::from_bits(nan0).next_up().to_bits(), nan0); - assert_eq!(f32::from_bits(nan1).next_up().to_bits(), nan1); - assert_eq!(f32::from_bits(nan2).next_up().to_bits(), nan2); + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); assert_eq!(f32::NEG_INFINITY.next_up(), f32::MIN); assert_eq!(f32::MIN.next_up(), -max_down); @@ -327,12 +327,12 @@ fn test_next_down() { let smallest_normal = f32::from_bits(0x0080_0000); // Check that NaNs roundtrip. - let nan0 = f32::NAN.to_bits(); - let nan1 = f32::NAN.to_bits() ^ 0x002a_aaaa; - let nan2 = f32::NAN.to_bits() ^ 0x0055_5555; - assert_eq!(f32::from_bits(nan0).next_down().to_bits(), nan0); - assert_eq!(f32::from_bits(nan1).next_down().to_bits(), nan1); - assert_eq!(f32::from_bits(nan2).next_down().to_bits(), nan2); + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); assert_eq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); assert_eq!(f32::MIN.next_down(), f32::NEG_INFINITY); diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 8e172cf4963d2..cf46ffcffcf2e 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -298,12 +298,12 @@ fn test_next_up() { let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); // Check that NaNs roundtrip. - let nan0 = f64::NAN.to_bits(); - let nan1 = f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa; - let nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; - assert_eq!(f64::from_bits(nan0).next_up().to_bits(), nan0); - assert_eq!(f64::from_bits(nan1).next_up().to_bits(), nan1); - assert_eq!(f64::from_bits(nan2).next_up().to_bits(), nan2); + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); assert_eq!(f64::NEG_INFINITY.next_up(), f64::MIN); assert_eq!(f64::MIN.next_up(), -max_down); @@ -329,12 +329,12 @@ fn test_next_down() { let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); // Check that NaNs roundtrip. - let nan0 = f64::NAN.to_bits(); - let nan1 = f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa; - let nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; - assert_eq!(f64::from_bits(nan0).next_down().to_bits(), nan0); - assert_eq!(f64::from_bits(nan1).next_down().to_bits(), nan1); - assert_eq!(f64::from_bits(nan2).next_down().to_bits(), nan2); + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); assert_eq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); assert_eq!(f64::MIN.next_down(), f64::NEG_INFINITY); From c4e0622869188fd94e0f1f455260ad8098cc1d06 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Mon, 29 Nov 2021 12:10:09 +0100 Subject: [PATCH 3/8] Ensure NaN references values go through function boundary for next_up/down. --- library/std/src/f32/tests.rs | 24 ++++++++++++++++++------ library/std/src/f64/tests.rs | 24 ++++++++++++++++++------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 938fd34d84f62..f748c2a38feb8 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -296,12 +296,18 @@ fn test_next_up() { let smallest_normal = f32::from_bits(0x0080_0000); // Check that NaNs roundtrip. + // Because x87 can lose NaN bits when passed through a function, ensure the reference value + // also passes through a function boundary. + #[inline(never)] + fn identity(x: f32) -> f32 { + crate::hint::black_box(x) + } let nan0 = f32::NAN; let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); - assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); + assert_eq!(nan0.next_up().to_bits(), identity(nan0).to_bits()); + assert_eq!(nan1.next_up().to_bits(), identity(nan1).to_bits()); + assert_eq!(nan2.next_up().to_bits(), identity(nan2).to_bits()); assert_eq!(f32::NEG_INFINITY.next_up(), f32::MIN); assert_eq!(f32::MIN.next_up(), -max_down); @@ -327,12 +333,18 @@ fn test_next_down() { let smallest_normal = f32::from_bits(0x0080_0000); // Check that NaNs roundtrip. + // Because x87 can lose NaN bits when passed through a function, ensure the reference value + // also passes through a function boundary. + #[inline(never)] + fn identity(x: f32) -> f32 { + crate::hint::black_box(x) + } let nan0 = f32::NAN; let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); - assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); + assert_eq!(nan0.next_down().to_bits(), identity(nan0).to_bits()); + assert_eq!(nan1.next_down().to_bits(), identity(nan1).to_bits()); + assert_eq!(nan2.next_down().to_bits(), identity(nan2).to_bits()); assert_eq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); assert_eq!(f32::MIN.next_down(), f32::NEG_INFINITY); diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index cf46ffcffcf2e..2524bfe8b9988 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -298,12 +298,18 @@ fn test_next_up() { let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); // Check that NaNs roundtrip. + // Because x87 can lose NaN bits when passed through a function, ensure the reference value + // also passes through a function boundary. + #[inline(never)] + fn identity(x: f64) -> f64 { + crate::hint::black_box(x) + } let nan0 = f64::NAN; let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); - assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); + assert_eq!(nan0.next_up().to_bits(), identity(nan0).to_bits()); + assert_eq!(nan1.next_up().to_bits(), identity(nan1).to_bits()); + assert_eq!(nan2.next_up().to_bits(), identity(nan2).to_bits()); assert_eq!(f64::NEG_INFINITY.next_up(), f64::MIN); assert_eq!(f64::MIN.next_up(), -max_down); @@ -329,12 +335,18 @@ fn test_next_down() { let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); // Check that NaNs roundtrip. + // Because x87 can lose NaN bits when passed through a function, ensure the reference value + // also passes through a function boundary. + #[inline(never)] + fn identity(x: f64) -> f64 { + crate::hint::black_box(x) + } let nan0 = f64::NAN; let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); - assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); + assert_eq!(nan0.next_down().to_bits(), identity(nan0).to_bits()); + assert_eq!(nan1.next_down().to_bits(), identity(nan1).to_bits()); + assert_eq!(nan2.next_down().to_bits(), identity(nan2).to_bits()); assert_eq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); assert_eq!(f64::MIN.next_down(), f64::NEG_INFINITY); From f020ba45125c0b69cb4d729003a1e371583107f0 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Tue, 30 Nov 2021 20:54:12 +0100 Subject: [PATCH 4/8] Added tracking issue numbers for float_next_up_down. --- library/core/src/num/f32.rs | 6 ++++-- library/core/src/num/f64.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index c5a23d4927d55..aeaba39217206 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -646,7 +646,8 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX - #[unstable(feature = "float_next_up_down", issue = "none")] + #[unstable(feature = "float_next_up_down", issue = "91399")] + #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // We must use strictly integer arithmetic to prevent denormals from // flushing to zero after an arithmetic operation on some platforms. @@ -695,7 +696,8 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX - #[unstable(feature = "float_next_up_down", issue = "none")] + #[unstable(feature = "float_next_up_down", issue = "91399")] + #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // We must use strictly integer arithmetic to prevent denormals from // flushing to zero after an arithmetic operation on some platforms. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index db09c8bb2430f..c9e8397fa0d05 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -661,7 +661,8 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX - #[unstable(feature = "float_next_up_down", issue = "none")] + #[unstable(feature = "float_next_up_down", issue = "91399")] + #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // We must use strictly integer arithmetic to prevent denormals from // flushing to zero after an arithmetic operation on some platforms. @@ -710,7 +711,8 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX - #[unstable(feature = "float_next_up_down", issue = "none")] + #[unstable(feature = "float_next_up_down", issue = "91399")] + #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // We must use strictly integer arithmetic to prevent denormals from // flushing to zero after an arithmetic operation on some platforms. From 1692bcc0c72db6079bd1d58c6cf56c0390e65e00 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Tue, 30 Nov 2021 21:08:37 +0100 Subject: [PATCH 5/8] Conditionally do not compile NaN roundtrip tests on x87 fp. --- library/std/src/f32/tests.rs | 44 ++++++++++++++++++------------------ library/std/src/f64/tests.rs | 44 ++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index f748c2a38feb8..9639dcd2ef1f6 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -296,18 +296,18 @@ fn test_next_up() { let smallest_normal = f32::from_bits(0x0080_0000); // Check that NaNs roundtrip. - // Because x87 can lose NaN bits when passed through a function, ensure the reference value - // also passes through a function boundary. - #[inline(never)] - fn identity(x: f32) -> f32 { - crate::hint::black_box(x) + // Ignore test on x87 floating point, the code is still correct but these + // platforms do not guarantee NaN payloads are preserved, which caused these + // tests to fail. + #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] + { + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); } - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); - assert_eq!(nan0.next_up().to_bits(), identity(nan0).to_bits()); - assert_eq!(nan1.next_up().to_bits(), identity(nan1).to_bits()); - assert_eq!(nan2.next_up().to_bits(), identity(nan2).to_bits()); assert_eq!(f32::NEG_INFINITY.next_up(), f32::MIN); assert_eq!(f32::MIN.next_up(), -max_down); @@ -333,18 +333,18 @@ fn test_next_down() { let smallest_normal = f32::from_bits(0x0080_0000); // Check that NaNs roundtrip. - // Because x87 can lose NaN bits when passed through a function, ensure the reference value - // also passes through a function boundary. - #[inline(never)] - fn identity(x: f32) -> f32 { - crate::hint::black_box(x) + // Ignore test on x87 floating point, the code is still correct but these + // platforms do not guarantee NaN payloads are preserved, which caused these + // tests to fail. + #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] + { + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); } - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); - assert_eq!(nan0.next_down().to_bits(), identity(nan0).to_bits()); - assert_eq!(nan1.next_down().to_bits(), identity(nan1).to_bits()); - assert_eq!(nan2.next_down().to_bits(), identity(nan2).to_bits()); assert_eq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); assert_eq!(f32::MIN.next_down(), f32::NEG_INFINITY); diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 2524bfe8b9988..e545a10c489d0 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -298,18 +298,18 @@ fn test_next_up() { let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); // Check that NaNs roundtrip. - // Because x87 can lose NaN bits when passed through a function, ensure the reference value - // also passes through a function boundary. - #[inline(never)] - fn identity(x: f64) -> f64 { - crate::hint::black_box(x) + // Ignore test on x87 floating point, the code is still correct but these + // platforms do not guarantee NaN payloads are preserved, which caused these + // tests to fail. + #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] + { + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); } - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); - assert_eq!(nan0.next_up().to_bits(), identity(nan0).to_bits()); - assert_eq!(nan1.next_up().to_bits(), identity(nan1).to_bits()); - assert_eq!(nan2.next_up().to_bits(), identity(nan2).to_bits()); assert_eq!(f64::NEG_INFINITY.next_up(), f64::MIN); assert_eq!(f64::MIN.next_up(), -max_down); @@ -335,18 +335,18 @@ fn test_next_down() { let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); // Check that NaNs roundtrip. - // Because x87 can lose NaN bits when passed through a function, ensure the reference value - // also passes through a function boundary. - #[inline(never)] - fn identity(x: f64) -> f64 { - crate::hint::black_box(x) + // Ignore test on x87 floating point, the code is still correct but these + // platforms do not guarantee NaN payloads are preserved, which caused these + // tests to fail. + #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] + { + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); + assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); + assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); } - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); - assert_eq!(nan0.next_down().to_bits(), identity(nan0).to_bits()); - assert_eq!(nan1.next_down().to_bits(), identity(nan1).to_bits()); - assert_eq!(nan2.next_down().to_bits(), identity(nan2).to_bits()); assert_eq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); assert_eq!(f64::MIN.next_down(), f64::NEG_INFINITY); From 9f38e1412da24e592f0a40f6a2bc8b231bed233b Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Sat, 19 Feb 2022 11:22:27 +0100 Subject: [PATCH 6/8] Skip next_up/down tests entirely on x87. --- library/std/src/f32/tests.rs | 108 ++++++++++++++++---------------- library/std/src/f64/tests.rs | 118 +++++++++++++++++------------------ 2 files changed, 114 insertions(+), 112 deletions(-) diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 9639dcd2ef1f6..f043df5b968c2 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -287,6 +287,19 @@ fn test_is_sign_negative() { assert!((-f32::NAN).is_sign_negative()); } +macro_rules! assert_f32_biteq { + ($left : expr, $right : expr) => { + let l: &f32 = &$left; + let r: &f32 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); + } +} + +// Ignore test on x87 floating point, these platforms do not guarantee NaN +// payloads are preserved and flush denormals to zero, failing the tests. +#[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { let tiny = f32::from_bits(1); @@ -294,36 +307,32 @@ fn test_next_up() { let max_down = f32::from_bits(0x7f7f_fffe); let largest_subnormal = f32::from_bits(0x007f_ffff); let smallest_normal = f32::from_bits(0x0080_0000); + assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); + assert_f32_biteq!(f32::MIN.next_up(), -max_down); + assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); + assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f32_biteq!((-tiny_up).next_up(), -tiny); + assert_f32_biteq!((-tiny).next_up(), -0.0f32); + assert_f32_biteq!((-0.0f32).next_up(), tiny); + assert_f32_biteq!(0.0f32.next_up(), tiny); + assert_f32_biteq!(tiny.next_up(), tiny_up); + assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); + assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); // Check that NaNs roundtrip. - // Ignore test on x87 floating point, the code is still correct but these - // platforms do not guarantee NaN payloads are preserved, which caused these - // tests to fail. - #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] - { - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); - assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); - } - - assert_eq!(f32::NEG_INFINITY.next_up(), f32::MIN); - assert_eq!(f32::MIN.next_up(), -max_down); - assert_eq!((-1.0 - f32::EPSILON).next_up(), -1.0); - assert_eq!((-smallest_normal).next_up(), -largest_subnormal); - assert_eq!((-tiny_up).next_up(), -tiny); - assert_eq!((-tiny).next_up().to_bits(), (-0.0f32).to_bits()); - assert_eq!((-0.0f32).next_up(), tiny); - assert_eq!(0.0f32.next_up(), tiny); - assert_eq!(tiny.next_up(), tiny_up); - assert_eq!(largest_subnormal.next_up(), smallest_normal); - assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON); - assert_eq!(f32::MAX.next_up(), f32::INFINITY); - assert_eq!(f32::INFINITY.next_up(), f32::INFINITY); + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + assert_f32_biteq!(nan0.next_up(), nan0); + assert_f32_biteq!(nan1.next_up(), nan1); + assert_f32_biteq!(nan2.next_up(), nan2); } +// Ignore test on x87 floating point, these platforms do not guarantee NaN +// payloads are preserved and flush denormals to zero, failing the tests. +#[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { let tiny = f32::from_bits(1); @@ -331,35 +340,28 @@ fn test_next_down() { let max_down = f32::from_bits(0x7f7f_fffe); let largest_subnormal = f32::from_bits(0x007f_ffff); let smallest_normal = f32::from_bits(0x0080_0000); + assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!((-max_down).next_down(), f32::MIN); + assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); + assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f32_biteq!((-tiny).next_down(), -tiny_up); + assert_f32_biteq!((-0.0f32).next_down(), -tiny); + assert_f32_biteq!((0.0f32).next_down(), -tiny); + assert_f32_biteq!(tiny.next_down(), 0.0f32); + assert_f32_biteq!(tiny_up.next_down(), tiny); + assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); + assert_f32_biteq!(f32::MAX.next_down(), max_down); + assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); // Check that NaNs roundtrip. - // Ignore test on x87 floating point, the code is still correct but these - // platforms do not guarantee NaN payloads are preserved, which caused these - // tests to fail. - #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] - { - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); - assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); - } - - assert_eq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); - assert_eq!(f32::MIN.next_down(), f32::NEG_INFINITY); - assert_eq!((-max_down).next_down(), f32::MIN); - assert_eq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); - assert_eq!((-largest_subnormal).next_down(), -smallest_normal); - assert_eq!((-tiny).next_down(), -tiny_up); - assert_eq!((-0.0f32).next_down(), -tiny); - assert_eq!((0.0f32).next_down(), -tiny); - assert_eq!(tiny.next_down().to_bits(), 0.0f32.to_bits()); - assert_eq!(tiny_up.next_down(), tiny); - assert_eq!(smallest_normal.next_down(), largest_subnormal); - assert_eq!((1.0 + f32::EPSILON).next_down(), 1.0f32); - assert_eq!(f32::MAX.next_down(), max_down); - assert_eq!(f32::INFINITY.next_down(), f32::MAX); + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + assert_f32_biteq!(nan0.next_down(), nan0); + assert_f32_biteq!(nan1.next_down(), nan1); + assert_f32_biteq!(nan2.next_down(), nan2); } #[test] diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index e545a10c489d0..8125b2492275b 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -289,6 +289,19 @@ fn test_is_sign_negative() { assert!((-f64::NAN).is_sign_negative()); } +macro_rules! assert_f64_biteq { + ($left : expr, $right : expr) => { + let l: &f64 = &$left; + let r: &f64 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); + } +} + +// Ignore test on x87 floating point, these platforms do not guarantee NaN +// payloads are preserved and flush denormals to zero, failing the tests. +#[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { let tiny = f64::from_bits(1); @@ -296,36 +309,31 @@ fn test_next_up() { let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); - - // Check that NaNs roundtrip. - // Ignore test on x87 floating point, the code is still correct but these - // platforms do not guarantee NaN payloads are preserved, which caused these - // tests to fail. - #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] - { - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); - assert_eq!(nan0.next_up().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_up().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_up().to_bits(), nan2.to_bits()); - } - - assert_eq!(f64::NEG_INFINITY.next_up(), f64::MIN); - assert_eq!(f64::MIN.next_up(), -max_down); - assert_eq!((-1.0 - f64::EPSILON).next_up(), -1.0); - assert_eq!((-smallest_normal).next_up(), -largest_subnormal); - assert_eq!((-tiny_up).next_up(), -tiny); - assert_eq!((-tiny).next_up().to_bits(), (-0.0f64).to_bits()); - assert_eq!((-0.0f64).next_up(), tiny); - assert_eq!(0.0f64.next_up(), tiny); - assert_eq!(tiny.next_up(), tiny_up); - assert_eq!(largest_subnormal.next_up(), smallest_normal); - assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON); - assert_eq!(f64::MAX.next_up(), f64::INFINITY); - assert_eq!(f64::INFINITY.next_up(), f64::INFINITY); -} - + assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); + assert_f64_biteq!(f64::MIN.next_up(), -max_down); + assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); + assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f64_biteq!((-tiny_up).next_up(), -tiny); + assert_f64_biteq!((-tiny).next_up(), -0.0f64); + assert_f64_biteq!((-0.0f64).next_up(), tiny); + assert_f64_biteq!(0.0f64.next_up(), tiny); + assert_f64_biteq!(tiny.next_up(), tiny_up); + assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); + assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + assert_f64_biteq!(nan0.next_up(), nan0); + assert_f64_biteq!(nan1.next_up(), nan1); + assert_f64_biteq!(nan2.next_up(), nan2); +} + +// Ignore test on x87 floating point, these platforms do not guarantee NaN +// payloads are preserved and flush denormals to zero, failing the tests. +#[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { let tiny = f64::from_bits(1); @@ -333,35 +341,27 @@ fn test_next_down() { let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); - - // Check that NaNs roundtrip. - // Ignore test on x87 floating point, the code is still correct but these - // platforms do not guarantee NaN payloads are preserved, which caused these - // tests to fail. - #[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))] - { - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); - assert_eq!(nan0.next_down().to_bits(), nan0.to_bits()); - assert_eq!(nan1.next_down().to_bits(), nan1.to_bits()); - assert_eq!(nan2.next_down().to_bits(), nan2.to_bits()); - } - - assert_eq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); - assert_eq!(f64::MIN.next_down(), f64::NEG_INFINITY); - assert_eq!((-max_down).next_down(), f64::MIN); - assert_eq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); - assert_eq!((-largest_subnormal).next_down(), -smallest_normal); - assert_eq!((-tiny).next_down(), -tiny_up); - assert_eq!((-0.0f64).next_down(), -tiny); - assert_eq!((0.0f64).next_down(), -tiny); - assert_eq!(tiny.next_down().to_bits(), 0.0f64.to_bits()); - assert_eq!(tiny_up.next_down(), tiny); - assert_eq!(smallest_normal.next_down(), largest_subnormal); - assert_eq!((1.0 + f64::EPSILON).next_down(), 1.0f64); - assert_eq!(f64::MAX.next_down(), max_down); - assert_eq!(f64::INFINITY.next_down(), f64::MAX); + assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!((-max_down).next_down(), f64::MIN); + assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); + assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f64_biteq!((-tiny).next_down(), -tiny_up); + assert_f64_biteq!((-0.0f64).next_down(), -tiny); + assert_f64_biteq!((0.0f64).next_down(), -tiny); + assert_f64_biteq!(tiny.next_down(), 0.0f64); + assert_f64_biteq!(tiny_up.next_down(), tiny); + assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); + assert_f64_biteq!(f64::MAX.next_down(), max_down); + assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + assert_f64_biteq!(nan0.next_down(), nan0); + assert_f64_biteq!(nan1.next_down(), nan1); + assert_f64_biteq!(nan2.next_down(), nan2); } #[test] From b2f56ace64f81fff322676e90398f05ca40f3a03 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Sat, 19 Feb 2022 12:23:54 +0100 Subject: [PATCH 7/8] Float biteq macros can be unused if test is skipped. --- library/std/src/f32/tests.rs | 1 + library/std/src/f64/tests.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index f043df5b968c2..54509a06ed928 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -287,6 +287,7 @@ fn test_is_sign_negative() { assert!((-f32::NAN).is_sign_negative()); } +#[allow(unused_macros)] macro_rules! assert_f32_biteq { ($left : expr, $right : expr) => { let l: &f32 = &$left; diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 8125b2492275b..1619288bedb19 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -289,6 +289,7 @@ fn test_is_sign_negative() { assert!((-f64::NAN).is_sign_negative()); } +#[allow(unused_macros)] macro_rules! assert_f64_biteq { ($left : expr, $right : expr) => { let l: &f64 = &$left; From 01951a09062cf655a43a21224e8fe3576429a30f Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Sat, 19 Feb 2022 19:05:55 +0100 Subject: [PATCH 8/8] Semicolon after macro_rules definition. --- library/std/src/f32/tests.rs | 2 +- library/std/src/f64/tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 54509a06ed928..ca94c353420f9 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -295,7 +295,7 @@ macro_rules! assert_f32_biteq { let lb = l.to_bits(); let rb = r.to_bits(); assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); - } + }; } // Ignore test on x87 floating point, these platforms do not guarantee NaN diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 1619288bedb19..12baa68f49b76 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -297,7 +297,7 @@ macro_rules! assert_f64_biteq { let lb = l.to_bits(); let rb = r.to_bits(); assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); - } + }; } // Ignore test on x87 floating point, these platforms do not guarantee NaN