Skip to content

Commit 22c78b9

Browse files
authored
Rollup merge of rust-lang#88728 - orlp:float-next-up-down, r=yaahc,scottmcm
Added next_up and next_down for f32/f64. This is a pull request implementing the features described at rust-lang/rfcs#3173.
2 parents c5ecc15 + 1692bcc commit 22c78b9

File tree

5 files changed

+351
-0
lines changed

5 files changed

+351
-0
lines changed

library/core/src/num/f32.rs

+100
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,106 @@ impl f32 {
620620
self.to_bits() & 0x8000_0000 != 0
621621
}
622622

623+
/// Returns the least number greater than `self`.
624+
///
625+
/// Let `TINY` be the smallest representable positive `f32`. Then,
626+
/// - if `self.is_nan()`, this returns `self`;
627+
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
628+
/// - if `self` is `-TINY`, this returns -0.0;
629+
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
630+
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
631+
/// - otherwise the unique least value greater than `self` is returned.
632+
///
633+
/// The identity `x.next_up() == -(-x).next_down()` holds for all `x`. When `x`
634+
/// is finite `x == x.next_up().next_down()` also holds.
635+
///
636+
/// ```rust
637+
/// #![feature(float_next_up_down)]
638+
/// // f32::EPSILON is the difference between 1.0 and the next number up.
639+
/// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
640+
/// // But not for most numbers.
641+
/// assert!(0.1f32.next_up() < 0.1 + f32::EPSILON);
642+
/// assert_eq!(16777216f32.next_up(), 16777218.0);
643+
/// ```
644+
///
645+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
646+
/// [`INFINITY`]: Self::INFINITY
647+
/// [`MIN`]: Self::MIN
648+
/// [`MAX`]: Self::MAX
649+
#[unstable(feature = "float_next_up_down", issue = "91399")]
650+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
651+
pub const fn next_up(self) -> Self {
652+
// We must use strictly integer arithmetic to prevent denormals from
653+
// flushing to zero after an arithmetic operation on some platforms.
654+
const TINY_BITS: u32 = 0x1; // Smallest positive f32.
655+
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
656+
657+
let bits = self.to_bits();
658+
if self.is_nan() || bits == Self::INFINITY.to_bits() {
659+
return self;
660+
}
661+
662+
let abs = bits & CLEAR_SIGN_MASK;
663+
let next_bits = if abs == 0 {
664+
TINY_BITS
665+
} else if bits == abs {
666+
bits + 1
667+
} else {
668+
bits - 1
669+
};
670+
Self::from_bits(next_bits)
671+
}
672+
673+
/// Returns the greatest number less than `self`.
674+
///
675+
/// Let `TINY` be the smallest representable positive `f32`. Then,
676+
/// - if `self.is_nan()`, this returns `self`;
677+
/// - if `self` is [`INFINITY`], this returns [`MAX`];
678+
/// - if `self` is `TINY`, this returns 0.0;
679+
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
680+
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
681+
/// - otherwise the unique greatest value less than `self` is returned.
682+
///
683+
/// The identity `x.next_down() == -(-x).next_up()` holds for all `x`. When `x`
684+
/// is finite `x == x.next_down().next_up()` also holds.
685+
///
686+
/// ```rust
687+
/// #![feature(float_next_up_down)]
688+
/// let x = 1.0f32;
689+
/// // Clamp value into range [0, 1).
690+
/// let clamped = x.clamp(0.0, 1.0f32.next_down());
691+
/// assert!(clamped < 1.0);
692+
/// assert_eq!(clamped.next_up(), 1.0);
693+
/// ```
694+
///
695+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
696+
/// [`INFINITY`]: Self::INFINITY
697+
/// [`MIN`]: Self::MIN
698+
/// [`MAX`]: Self::MAX
699+
#[unstable(feature = "float_next_up_down", issue = "91399")]
700+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
701+
pub const fn next_down(self) -> Self {
702+
// We must use strictly integer arithmetic to prevent denormals from
703+
// flushing to zero after an arithmetic operation on some platforms.
704+
const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32.
705+
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
706+
707+
let bits = self.to_bits();
708+
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
709+
return self;
710+
}
711+
712+
let abs = bits & CLEAR_SIGN_MASK;
713+
let next_bits = if abs == 0 {
714+
NEG_TINY_BITS
715+
} else if bits == abs {
716+
bits - 1
717+
} else {
718+
bits + 1
719+
};
720+
Self::from_bits(next_bits)
721+
}
722+
623723
/// Takes the reciprocal (inverse) of a number, `1/x`.
624724
///
625725
/// ```

library/core/src/num/f64.rs

+100
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,106 @@ impl f64 {
635635
self.is_sign_negative()
636636
}
637637

638+
/// Returns the least number greater than `self`.
639+
///
640+
/// Let `TINY` be the smallest representable positive `f64`. Then,
641+
/// - if `self.is_nan()`, this returns `self`;
642+
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
643+
/// - if `self` is `-TINY`, this returns -0.0;
644+
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
645+
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
646+
/// - otherwise the unique least value greater than `self` is returned.
647+
///
648+
/// The identity `x.next_up() == -(-x).next_down()` holds for all `x`. When `x`
649+
/// is finite `x == x.next_up().next_down()` also holds.
650+
///
651+
/// ```rust
652+
/// #![feature(float_next_up_down)]
653+
/// // f64::EPSILON is the difference between 1.0 and the next number up.
654+
/// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
655+
/// // But not for most numbers.
656+
/// assert!(0.1f64.next_up() < 0.1 + f64::EPSILON);
657+
/// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0);
658+
/// ```
659+
///
660+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
661+
/// [`INFINITY`]: Self::INFINITY
662+
/// [`MIN`]: Self::MIN
663+
/// [`MAX`]: Self::MAX
664+
#[unstable(feature = "float_next_up_down", issue = "91399")]
665+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
666+
pub const fn next_up(self) -> Self {
667+
// We must use strictly integer arithmetic to prevent denormals from
668+
// flushing to zero after an arithmetic operation on some platforms.
669+
const TINY_BITS: u64 = 0x1; // Smallest positive f64.
670+
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
671+
672+
let bits = self.to_bits();
673+
if self.is_nan() || bits == Self::INFINITY.to_bits() {
674+
return self;
675+
}
676+
677+
let abs = bits & CLEAR_SIGN_MASK;
678+
let next_bits = if abs == 0 {
679+
TINY_BITS
680+
} else if bits == abs {
681+
bits + 1
682+
} else {
683+
bits - 1
684+
};
685+
Self::from_bits(next_bits)
686+
}
687+
688+
/// Returns the greatest number less than `self`.
689+
///
690+
/// Let `TINY` be the smallest representable positive `f64`. Then,
691+
/// - if `self.is_nan()`, this returns `self`;
692+
/// - if `self` is [`INFINITY`], this returns [`MAX`];
693+
/// - if `self` is `TINY`, this returns 0.0;
694+
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
695+
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
696+
/// - otherwise the unique greatest value less than `self` is returned.
697+
///
698+
/// The identity `x.next_down() == -(-x).next_up()` holds for all `x`. When `x`
699+
/// is finite `x == x.next_down().next_up()` also holds.
700+
///
701+
/// ```rust
702+
/// #![feature(float_next_up_down)]
703+
/// let x = 1.0f64;
704+
/// // Clamp value into range [0, 1).
705+
/// let clamped = x.clamp(0.0, 1.0f64.next_down());
706+
/// assert!(clamped < 1.0);
707+
/// assert_eq!(clamped.next_up(), 1.0);
708+
/// ```
709+
///
710+
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
711+
/// [`INFINITY`]: Self::INFINITY
712+
/// [`MIN`]: Self::MIN
713+
/// [`MAX`]: Self::MAX
714+
#[unstable(feature = "float_next_up_down", issue = "91399")]
715+
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
716+
pub const fn next_down(self) -> Self {
717+
// We must use strictly integer arithmetic to prevent denormals from
718+
// flushing to zero after an arithmetic operation on some platforms.
719+
const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
720+
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
721+
722+
let bits = self.to_bits();
723+
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
724+
return self;
725+
}
726+
727+
let abs = bits & CLEAR_SIGN_MASK;
728+
let next_bits = if abs == 0 {
729+
NEG_TINY_BITS
730+
} else if bits == abs {
731+
bits - 1
732+
} else {
733+
bits + 1
734+
};
735+
Self::from_bits(next_bits)
736+
}
737+
638738
/// Takes the reciprocal (inverse) of a number, `1/x`.
639739
///
640740
/// ```

library/std/src/f32/tests.rs

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

302+
#[test]
303+
fn test_next_up() {
304+
let tiny = f32::from_bits(1);
305+
let tiny_up = f32::from_bits(2);
306+
let max_down = f32::from_bits(0x7f7f_fffe);
307+
let largest_subnormal = f32::from_bits(0x007f_ffff);
308+
let smallest_normal = f32::from_bits(0x0080_0000);
309+
310+
// Check that NaNs roundtrip.
311+
// Ignore test on x87 floating point, the code is still correct but these
312+
// platforms do not guarantee NaN payloads are preserved, which caused these
313+
// tests to fail.
314+
#[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))]
315+
{
316+
let nan0 = f32::NAN;
317+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
318+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
319+
assert_eq!(nan0.next_up().to_bits(), nan0.to_bits());
320+
assert_eq!(nan1.next_up().to_bits(), nan1.to_bits());
321+
assert_eq!(nan2.next_up().to_bits(), nan2.to_bits());
322+
}
323+
324+
assert_eq!(f32::NEG_INFINITY.next_up(), f32::MIN);
325+
assert_eq!(f32::MIN.next_up(), -max_down);
326+
assert_eq!((-1.0 - f32::EPSILON).next_up(), -1.0);
327+
assert_eq!((-smallest_normal).next_up(), -largest_subnormal);
328+
assert_eq!((-tiny_up).next_up(), -tiny);
329+
assert_eq!((-tiny).next_up().to_bits(), (-0.0f32).to_bits());
330+
assert_eq!((-0.0f32).next_up(), tiny);
331+
assert_eq!(0.0f32.next_up(), tiny);
332+
assert_eq!(tiny.next_up(), tiny_up);
333+
assert_eq!(largest_subnormal.next_up(), smallest_normal);
334+
assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
335+
assert_eq!(f32::MAX.next_up(), f32::INFINITY);
336+
assert_eq!(f32::INFINITY.next_up(), f32::INFINITY);
337+
}
338+
339+
#[test]
340+
fn test_next_down() {
341+
let tiny = f32::from_bits(1);
342+
let tiny_up = f32::from_bits(2);
343+
let max_down = f32::from_bits(0x7f7f_fffe);
344+
let largest_subnormal = f32::from_bits(0x007f_ffff);
345+
let smallest_normal = f32::from_bits(0x0080_0000);
346+
347+
// Check that NaNs roundtrip.
348+
// Ignore test on x87 floating point, the code is still correct but these
349+
// platforms do not guarantee NaN payloads are preserved, which caused these
350+
// tests to fail.
351+
#[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))]
352+
{
353+
let nan0 = f32::NAN;
354+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
355+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
356+
assert_eq!(nan0.next_down().to_bits(), nan0.to_bits());
357+
assert_eq!(nan1.next_down().to_bits(), nan1.to_bits());
358+
assert_eq!(nan2.next_down().to_bits(), nan2.to_bits());
359+
}
360+
361+
assert_eq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY);
362+
assert_eq!(f32::MIN.next_down(), f32::NEG_INFINITY);
363+
assert_eq!((-max_down).next_down(), f32::MIN);
364+
assert_eq!((-1.0f32).next_down(), -1.0 - f32::EPSILON);
365+
assert_eq!((-largest_subnormal).next_down(), -smallest_normal);
366+
assert_eq!((-tiny).next_down(), -tiny_up);
367+
assert_eq!((-0.0f32).next_down(), -tiny);
368+
assert_eq!((0.0f32).next_down(), -tiny);
369+
assert_eq!(tiny.next_down().to_bits(), 0.0f32.to_bits());
370+
assert_eq!(tiny_up.next_down(), tiny);
371+
assert_eq!(smallest_normal.next_down(), largest_subnormal);
372+
assert_eq!((1.0 + f32::EPSILON).next_down(), 1.0f32);
373+
assert_eq!(f32::MAX.next_down(), max_down);
374+
assert_eq!(f32::INFINITY.next_down(), f32::MAX);
375+
}
376+
302377
#[test]
303378
fn test_mul_add() {
304379
let nan: f32 = f32::NAN;

library/std/src/f64/tests.rs

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

292+
#[test]
293+
fn test_next_up() {
294+
let tiny = f64::from_bits(1);
295+
let tiny_up = f64::from_bits(2);
296+
let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
297+
let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
298+
let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
299+
300+
// Check that NaNs roundtrip.
301+
// Ignore test on x87 floating point, the code is still correct but these
302+
// platforms do not guarantee NaN payloads are preserved, which caused these
303+
// tests to fail.
304+
#[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))]
305+
{
306+
let nan0 = f64::NAN;
307+
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
308+
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
309+
assert_eq!(nan0.next_up().to_bits(), nan0.to_bits());
310+
assert_eq!(nan1.next_up().to_bits(), nan1.to_bits());
311+
assert_eq!(nan2.next_up().to_bits(), nan2.to_bits());
312+
}
313+
314+
assert_eq!(f64::NEG_INFINITY.next_up(), f64::MIN);
315+
assert_eq!(f64::MIN.next_up(), -max_down);
316+
assert_eq!((-1.0 - f64::EPSILON).next_up(), -1.0);
317+
assert_eq!((-smallest_normal).next_up(), -largest_subnormal);
318+
assert_eq!((-tiny_up).next_up(), -tiny);
319+
assert_eq!((-tiny).next_up().to_bits(), (-0.0f64).to_bits());
320+
assert_eq!((-0.0f64).next_up(), tiny);
321+
assert_eq!(0.0f64.next_up(), tiny);
322+
assert_eq!(tiny.next_up(), tiny_up);
323+
assert_eq!(largest_subnormal.next_up(), smallest_normal);
324+
assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
325+
assert_eq!(f64::MAX.next_up(), f64::INFINITY);
326+
assert_eq!(f64::INFINITY.next_up(), f64::INFINITY);
327+
}
328+
329+
#[test]
330+
fn test_next_down() {
331+
let tiny = f64::from_bits(1);
332+
let tiny_up = f64::from_bits(2);
333+
let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
334+
let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
335+
let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
336+
337+
// Check that NaNs roundtrip.
338+
// Ignore test on x87 floating point, the code is still correct but these
339+
// platforms do not guarantee NaN payloads are preserved, which caused these
340+
// tests to fail.
341+
#[cfg(not(all(target_arch = "x86", not(target_feature = "fxsr"))))]
342+
{
343+
let nan0 = f64::NAN;
344+
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
345+
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
346+
assert_eq!(nan0.next_down().to_bits(), nan0.to_bits());
347+
assert_eq!(nan1.next_down().to_bits(), nan1.to_bits());
348+
assert_eq!(nan2.next_down().to_bits(), nan2.to_bits());
349+
}
350+
351+
assert_eq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY);
352+
assert_eq!(f64::MIN.next_down(), f64::NEG_INFINITY);
353+
assert_eq!((-max_down).next_down(), f64::MIN);
354+
assert_eq!((-1.0f64).next_down(), -1.0 - f64::EPSILON);
355+
assert_eq!((-largest_subnormal).next_down(), -smallest_normal);
356+
assert_eq!((-tiny).next_down(), -tiny_up);
357+
assert_eq!((-0.0f64).next_down(), -tiny);
358+
assert_eq!((0.0f64).next_down(), -tiny);
359+
assert_eq!(tiny.next_down().to_bits(), 0.0f64.to_bits());
360+
assert_eq!(tiny_up.next_down(), tiny);
361+
assert_eq!(smallest_normal.next_down(), largest_subnormal);
362+
assert_eq!((1.0 + f64::EPSILON).next_down(), 1.0f64);
363+
assert_eq!(f64::MAX.next_down(), max_down);
364+
assert_eq!(f64::INFINITY.next_down(), f64::MAX);
365+
}
366+
292367
#[test]
293368
fn test_mul_add() {
294369
let nan: f64 = f64::NAN;

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@
280280
#![feature(exact_size_is_empty)]
281281
#![feature(exhaustive_patterns)]
282282
#![feature(extend_one)]
283+
#![feature(float_next_up_down)]
283284
#![feature(fn_traits)]
284285
#![feature(float_minimum_maximum)]
285286
#![feature(format_args_nl)]

0 commit comments

Comments
 (0)