Skip to content

Commit

Permalink
Rollup merge of rust-lang#88728 - orlp:float-next-up-down, r=yaahc
Browse files Browse the repository at this point in the history
Added next_up and next_down for f32/f64.

This is a pull request implementing the features described at rust-lang/rfcs#3173.
  • Loading branch information
matthiaskrgr committed Oct 22, 2021
2 parents f24b5da + 4dd9b85 commit 6e6adb4
Show file tree
Hide file tree
Showing 5 changed files with 323 additions and 0 deletions.
98 changes: 98 additions & 0 deletions library/core/src/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
///
/// ```
Expand Down
98 changes: 98 additions & 0 deletions library/core/src/num/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
///
/// ```
Expand Down
63 changes: 63 additions & 0 deletions library/std/src/f32/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
63 changes: 63 additions & 0 deletions library/std/src/f64/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@
#![feature(exhaustive_patterns)]
#![feature(extend_one)]
#![feature(float_interpolation)]
#![feature(float_next_up_down)]
#![feature(fn_traits)]
#![feature(format_args_nl)]
#![feature(gen_future)]
Expand Down

0 comments on commit 6e6adb4

Please sign in to comment.