From 04681898f09dad23b2d282a541e1a3b181bed33e Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Tue, 7 Sep 2021 18:46:49 +0200 Subject: [PATCH] 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 6548ad2e514f..7c38d830636d 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -678,6 +678,104 @@ pub const fn is_sign_negative(self) -> bool { unsafe { mem::transmute::(self) & 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 75c92c2f8834..8146f0075316 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -688,6 +688,104 @@ pub fn is_negative(self) -> bool { 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 69fa203ff4e7..19de0cf6c751 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -299,6 +299,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 5c163cfe90e0..8e172cf4963d 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 475a1d9fd992..e0473f746e54 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -273,6 +273,7 @@ #![feature(exclusive_wrapper)] #![feature(extend_one)] #![feature(float_minimum_maximum)] +#![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(int_error_internals)]