mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 15:01:51 +00:00
Auto merge of #100578 - Urgau:float-next-up-down, r=scottmcm
Add next_up and next_down for f32/f64 - take 2 This is a revival of https://github.com/rust-lang/rust/pull/88728 which staled due to inactivity of the original author. I've address the last review comment. --- This is a pull request implementing the features described at https://github.com/rust-lang/rfcs/pull/3173. `@rustbot` label +T-libs-api -T-libs r? `@scottmcm` cc `@orlp`
This commit is contained in:
commit
1ea4efd065
@ -678,6 +678,106 @@ impl f32 {
|
||||
unsafe { mem::transmute::<f32, u32>(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 non-NaN `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 = "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.
|
||||
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 non-NaN `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 = "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.
|
||||
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`.
|
||||
///
|
||||
/// ```
|
||||
|
@ -688,6 +688,106 @@ 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 non-NaN `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 = "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.
|
||||
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 non-NaN `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 = "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.
|
||||
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`.
|
||||
///
|
||||
/// ```
|
||||
|
@ -299,6 +299,84 @@ 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;
|
||||
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);
|
||||
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);
|
||||
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.
|
||||
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);
|
||||
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);
|
||||
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.
|
||||
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]
|
||||
fn test_mul_add() {
|
||||
let nan: f32 = f32::NAN;
|
||||
|
@ -289,6 +289,82 @@ 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;
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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]
|
||||
fn test_mul_add() {
|
||||
let nan: f64 = f64::NAN;
|
||||
|
@ -288,6 +288,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)]
|
||||
|
Loading…
Reference in New Issue
Block a user