Skip to content

Commit 1ea4efd

Browse files
committed
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 #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 rust-lang/rfcs#3173. `@rustbot` label +T-libs-api -T-libs r? `@scottmcm` cc `@orlp`
2 parents ce36e88 + 3f10e6c commit 1ea4efd

File tree

5 files changed

+355
-0
lines changed

5 files changed

+355
-0
lines changed

library/core/src/num/f32.rs

+100
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,106 @@ impl f32 {
678678
unsafe { mem::transmute::<f32, u32>(self) & 0x8000_0000 != 0 }
679679
}
680680

681+
/// Returns the least number greater than `self`.
682+
///
683+
/// Let `TINY` be the smallest representable positive `f32`. Then,
684+
/// - if `self.is_nan()`, this returns `self`;
685+
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
686+
/// - if `self` is `-TINY`, this returns -0.0;
687+
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
688+
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
689+
/// - otherwise the unique least value greater than `self` is returned.
690+
///
691+
/// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
692+
/// is finite `x == x.next_up().next_down()` also holds.
693+
///
694+
/// ```rust
695+
/// #![feature(float_next_up_down)]
696+
/// // f32::EPSILON is the difference between 1.0 and the next number up.
697+
/// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
698+
/// // But not for most numbers.
699+
/// assert!(0.1f32.next_up() < 0.1 + f32::EPSILON);
700+
/// assert_eq!(16777216f32.next_up(), 16777218.0);
701+
/// ```
702+
///
703+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
704+
/// [`INFINITY`]: Self::INFINITY
705+
/// [`MIN`]: Self::MIN
706+
/// [`MAX`]: Self::MAX
707+
#[unstable(feature = "float_next_up_down", issue = "91399")]
708+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
709+
pub const fn next_up(self) -> Self {
710+
// We must use strictly integer arithmetic to prevent denormals from
711+
// flushing to zero after an arithmetic operation on some platforms.
712+
const TINY_BITS: u32 = 0x1; // Smallest positive f32.
713+
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
714+
715+
let bits = self.to_bits();
716+
if self.is_nan() || bits == Self::INFINITY.to_bits() {
717+
return self;
718+
}
719+
720+
let abs = bits & CLEAR_SIGN_MASK;
721+
let next_bits = if abs == 0 {
722+
TINY_BITS
723+
} else if bits == abs {
724+
bits + 1
725+
} else {
726+
bits - 1
727+
};
728+
Self::from_bits(next_bits)
729+
}
730+
731+
/// Returns the greatest number less than `self`.
732+
///
733+
/// Let `TINY` be the smallest representable positive `f32`. Then,
734+
/// - if `self.is_nan()`, this returns `self`;
735+
/// - if `self` is [`INFINITY`], this returns [`MAX`];
736+
/// - if `self` is `TINY`, this returns 0.0;
737+
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
738+
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
739+
/// - otherwise the unique greatest value less than `self` is returned.
740+
///
741+
/// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
742+
/// is finite `x == x.next_down().next_up()` also holds.
743+
///
744+
/// ```rust
745+
/// #![feature(float_next_up_down)]
746+
/// let x = 1.0f32;
747+
/// // Clamp value into range [0, 1).
748+
/// let clamped = x.clamp(0.0, 1.0f32.next_down());
749+
/// assert!(clamped < 1.0);
750+
/// assert_eq!(clamped.next_up(), 1.0);
751+
/// ```
752+
///
753+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
754+
/// [`INFINITY`]: Self::INFINITY
755+
/// [`MIN`]: Self::MIN
756+
/// [`MAX`]: Self::MAX
757+
#[unstable(feature = "float_next_up_down", issue = "91399")]
758+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
759+
pub const fn next_down(self) -> Self {
760+
// We must use strictly integer arithmetic to prevent denormals from
761+
// flushing to zero after an arithmetic operation on some platforms.
762+
const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32.
763+
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
764+
765+
let bits = self.to_bits();
766+
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
767+
return self;
768+
}
769+
770+
let abs = bits & CLEAR_SIGN_MASK;
771+
let next_bits = if abs == 0 {
772+
NEG_TINY_BITS
773+
} else if bits == abs {
774+
bits - 1
775+
} else {
776+
bits + 1
777+
};
778+
Self::from_bits(next_bits)
779+
}
780+
681781
/// Takes the reciprocal (inverse) of a number, `1/x`.
682782
///
683783
/// ```

library/core/src/num/f64.rs

+100
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,106 @@ impl f64 {
688688
self.is_sign_negative()
689689
}
690690

691+
/// Returns the least number greater than `self`.
692+
///
693+
/// Let `TINY` be the smallest representable positive `f64`. Then,
694+
/// - if `self.is_nan()`, this returns `self`;
695+
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
696+
/// - if `self` is `-TINY`, this returns -0.0;
697+
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
698+
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
699+
/// - otherwise the unique least value greater than `self` is returned.
700+
///
701+
/// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
702+
/// is finite `x == x.next_up().next_down()` also holds.
703+
///
704+
/// ```rust
705+
/// #![feature(float_next_up_down)]
706+
/// // f64::EPSILON is the difference between 1.0 and the next number up.
707+
/// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
708+
/// // But not for most numbers.
709+
/// assert!(0.1f64.next_up() < 0.1 + f64::EPSILON);
710+
/// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0);
711+
/// ```
712+
///
713+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
714+
/// [`INFINITY`]: Self::INFINITY
715+
/// [`MIN`]: Self::MIN
716+
/// [`MAX`]: Self::MAX
717+
#[unstable(feature = "float_next_up_down", issue = "91399")]
718+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
719+
pub const fn next_up(self) -> Self {
720+
// We must use strictly integer arithmetic to prevent denormals from
721+
// flushing to zero after an arithmetic operation on some platforms.
722+
const TINY_BITS: u64 = 0x1; // Smallest positive f64.
723+
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
724+
725+
let bits = self.to_bits();
726+
if self.is_nan() || bits == Self::INFINITY.to_bits() {
727+
return self;
728+
}
729+
730+
let abs = bits & CLEAR_SIGN_MASK;
731+
let next_bits = if abs == 0 {
732+
TINY_BITS
733+
} else if bits == abs {
734+
bits + 1
735+
} else {
736+
bits - 1
737+
};
738+
Self::from_bits(next_bits)
739+
}
740+
741+
/// Returns the greatest number less than `self`.
742+
///
743+
/// Let `TINY` be the smallest representable positive `f64`. Then,
744+
/// - if `self.is_nan()`, this returns `self`;
745+
/// - if `self` is [`INFINITY`], this returns [`MAX`];
746+
/// - if `self` is `TINY`, this returns 0.0;
747+
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
748+
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
749+
/// - otherwise the unique greatest value less than `self` is returned.
750+
///
751+
/// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
752+
/// is finite `x == x.next_down().next_up()` also holds.
753+
///
754+
/// ```rust
755+
/// #![feature(float_next_up_down)]
756+
/// let x = 1.0f64;
757+
/// // Clamp value into range [0, 1).
758+
/// let clamped = x.clamp(0.0, 1.0f64.next_down());
759+
/// assert!(clamped < 1.0);
760+
/// assert_eq!(clamped.next_up(), 1.0);
761+
/// ```
762+
///
763+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
764+
/// [`INFINITY`]: Self::INFINITY
765+
/// [`MIN`]: Self::MIN
766+
/// [`MAX`]: Self::MAX
767+
#[unstable(feature = "float_next_up_down", issue = "91399")]
768+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
769+
pub const fn next_down(self) -> Self {
770+
// We must use strictly integer arithmetic to prevent denormals from
771+
// flushing to zero after an arithmetic operation on some platforms.
772+
const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
773+
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
774+
775+
let bits = self.to_bits();
776+
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
777+
return self;
778+
}
779+
780+
let abs = bits & CLEAR_SIGN_MASK;
781+
let next_bits = if abs == 0 {
782+
NEG_TINY_BITS
783+
} else if bits == abs {
784+
bits - 1
785+
} else {
786+
bits + 1
787+
};
788+
Self::from_bits(next_bits)
789+
}
790+
691791
/// Takes the reciprocal (inverse) of a number, `1/x`.
692792
///
693793
/// ```

library/std/src/f32/tests.rs

+78
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,84 @@ fn test_is_sign_negative() {
299299
assert!((-f32::NAN).is_sign_negative());
300300
}
301301

302+
#[allow(unused_macros)]
303+
macro_rules! assert_f32_biteq {
304+
($left : expr, $right : expr) => {
305+
let l: &f32 = &$left;
306+
let r: &f32 = &$right;
307+
let lb = l.to_bits();
308+
let rb = r.to_bits();
309+
assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
310+
};
311+
}
312+
313+
// Ignore test on x87 floating point, these platforms do not guarantee NaN
314+
// payloads are preserved and flush denormals to zero, failing the tests.
315+
#[cfg(not(target_arch = "x86"))]
316+
#[test]
317+
fn test_next_up() {
318+
let tiny = f32::from_bits(1);
319+
let tiny_up = f32::from_bits(2);
320+
let max_down = f32::from_bits(0x7f7f_fffe);
321+
let largest_subnormal = f32::from_bits(0x007f_ffff);
322+
let smallest_normal = f32::from_bits(0x0080_0000);
323+
assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN);
324+
assert_f32_biteq!(f32::MIN.next_up(), -max_down);
325+
assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0);
326+
assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal);
327+
assert_f32_biteq!((-tiny_up).next_up(), -tiny);
328+
assert_f32_biteq!((-tiny).next_up(), -0.0f32);
329+
assert_f32_biteq!((-0.0f32).next_up(), tiny);
330+
assert_f32_biteq!(0.0f32.next_up(), tiny);
331+
assert_f32_biteq!(tiny.next_up(), tiny_up);
332+
assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal);
333+
assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
334+
assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY);
335+
assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY);
336+
337+
// Check that NaNs roundtrip.
338+
let nan0 = f32::NAN;
339+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
340+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
341+
assert_f32_biteq!(nan0.next_up(), nan0);
342+
assert_f32_biteq!(nan1.next_up(), nan1);
343+
assert_f32_biteq!(nan2.next_up(), nan2);
344+
}
345+
346+
// Ignore test on x87 floating point, these platforms do not guarantee NaN
347+
// payloads are preserved and flush denormals to zero, failing the tests.
348+
#[cfg(not(target_arch = "x86"))]
349+
#[test]
350+
fn test_next_down() {
351+
let tiny = f32::from_bits(1);
352+
let tiny_up = f32::from_bits(2);
353+
let max_down = f32::from_bits(0x7f7f_fffe);
354+
let largest_subnormal = f32::from_bits(0x007f_ffff);
355+
let smallest_normal = f32::from_bits(0x0080_0000);
356+
assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY);
357+
assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY);
358+
assert_f32_biteq!((-max_down).next_down(), f32::MIN);
359+
assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON);
360+
assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal);
361+
assert_f32_biteq!((-tiny).next_down(), -tiny_up);
362+
assert_f32_biteq!((-0.0f32).next_down(), -tiny);
363+
assert_f32_biteq!((0.0f32).next_down(), -tiny);
364+
assert_f32_biteq!(tiny.next_down(), 0.0f32);
365+
assert_f32_biteq!(tiny_up.next_down(), tiny);
366+
assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal);
367+
assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32);
368+
assert_f32_biteq!(f32::MAX.next_down(), max_down);
369+
assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX);
370+
371+
// Check that NaNs roundtrip.
372+
let nan0 = f32::NAN;
373+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
374+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
375+
assert_f32_biteq!(nan0.next_down(), nan0);
376+
assert_f32_biteq!(nan1.next_down(), nan1);
377+
assert_f32_biteq!(nan2.next_down(), nan2);
378+
}
379+
302380
#[test]
303381
fn test_mul_add() {
304382
let nan: f32 = f32::NAN;

library/std/src/f64/tests.rs

+76
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,82 @@ fn test_is_sign_negative() {
289289
assert!((-f64::NAN).is_sign_negative());
290290
}
291291

292+
#[allow(unused_macros)]
293+
macro_rules! assert_f64_biteq {
294+
($left : expr, $right : expr) => {
295+
let l: &f64 = &$left;
296+
let r: &f64 = &$right;
297+
let lb = l.to_bits();
298+
let rb = r.to_bits();
299+
assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
300+
};
301+
}
302+
303+
// Ignore test on x87 floating point, these platforms do not guarantee NaN
304+
// payloads are preserved and flush denormals to zero, failing the tests.
305+
#[cfg(not(target_arch = "x86"))]
306+
#[test]
307+
fn test_next_up() {
308+
let tiny = f64::from_bits(1);
309+
let tiny_up = f64::from_bits(2);
310+
let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
311+
let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
312+
let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
313+
assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN);
314+
assert_f64_biteq!(f64::MIN.next_up(), -max_down);
315+
assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0);
316+
assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal);
317+
assert_f64_biteq!((-tiny_up).next_up(), -tiny);
318+
assert_f64_biteq!((-tiny).next_up(), -0.0f64);
319+
assert_f64_biteq!((-0.0f64).next_up(), tiny);
320+
assert_f64_biteq!(0.0f64.next_up(), tiny);
321+
assert_f64_biteq!(tiny.next_up(), tiny_up);
322+
assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal);
323+
assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
324+
assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY);
325+
assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY);
326+
327+
let nan0 = f64::NAN;
328+
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
329+
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
330+
assert_f64_biteq!(nan0.next_up(), nan0);
331+
assert_f64_biteq!(nan1.next_up(), nan1);
332+
assert_f64_biteq!(nan2.next_up(), nan2);
333+
}
334+
335+
// Ignore test on x87 floating point, these platforms do not guarantee NaN
336+
// payloads are preserved and flush denormals to zero, failing the tests.
337+
#[cfg(not(target_arch = "x86"))]
338+
#[test]
339+
fn test_next_down() {
340+
let tiny = f64::from_bits(1);
341+
let tiny_up = f64::from_bits(2);
342+
let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
343+
let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
344+
let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
345+
assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY);
346+
assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY);
347+
assert_f64_biteq!((-max_down).next_down(), f64::MIN);
348+
assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON);
349+
assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal);
350+
assert_f64_biteq!((-tiny).next_down(), -tiny_up);
351+
assert_f64_biteq!((-0.0f64).next_down(), -tiny);
352+
assert_f64_biteq!((0.0f64).next_down(), -tiny);
353+
assert_f64_biteq!(tiny.next_down(), 0.0f64);
354+
assert_f64_biteq!(tiny_up.next_down(), tiny);
355+
assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal);
356+
assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64);
357+
assert_f64_biteq!(f64::MAX.next_down(), max_down);
358+
assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX);
359+
360+
let nan0 = f64::NAN;
361+
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
362+
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
363+
assert_f64_biteq!(nan0.next_down(), nan0);
364+
assert_f64_biteq!(nan1.next_down(), nan1);
365+
assert_f64_biteq!(nan2.next_down(), nan2);
366+
}
367+
292368
#[test]
293369
fn test_mul_add() {
294370
let nan: f64 = f64::NAN;

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@
288288
#![feature(exclusive_wrapper)]
289289
#![feature(extend_one)]
290290
#![feature(float_minimum_maximum)]
291+
#![feature(float_next_up_down)]
291292
#![feature(hasher_prefixfree_extras)]
292293
#![feature(hashmap_internals)]
293294
#![feature(int_error_internals)]

0 commit comments

Comments
 (0)