Skip to content

Commit b190746

Browse files
committed
Extract repeated constants from f32 and f64 source
This will make it easier to keep `f16` and `f128` consistent as their implementations get added.
1 parent af3d100 commit b190746

File tree

4 files changed

+118
-89
lines changed

4 files changed

+118
-89
lines changed

library/core/src/num/f32.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,15 @@ impl f32 {
490490
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
491491
pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32;
492492

493+
/// Sign bit
494+
const SIGN_MASK: u32 = 0x8000_0000;
495+
496+
/// Exponent mask
497+
const EXP_MASK: u32 = 0x7f80_0000;
498+
499+
/// Mantissa mask
500+
const MAN_MASK: u32 = 0x007f_ffff;
501+
493502
/// Returns `true` if this value is NaN.
494503
///
495504
/// ```
@@ -515,7 +524,7 @@ impl f32 {
515524
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
516525
pub(crate) const fn abs_private(self) -> f32 {
517526
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
518-
unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & 0x7fff_ffff) }
527+
unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & !Self::SIGN_MASK) }
519528
}
520529

521530
/// Returns `true` if this value is positive infinity or negative infinity, and
@@ -682,12 +691,9 @@ impl f32 {
682691
// runtime-deviating logic which may or may not be acceptable.
683692
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
684693
const unsafe fn partial_classify(self) -> FpCategory {
685-
const EXP_MASK: u32 = 0x7f800000;
686-
const MAN_MASK: u32 = 0x007fffff;
687-
688694
// SAFETY: The caller is not asking questions for which this will tell lies.
689695
let b = unsafe { mem::transmute::<f32, u32>(self) };
690-
match (b & MAN_MASK, b & EXP_MASK) {
696+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
691697
(0, 0) => FpCategory::Zero,
692698
(_, 0) => FpCategory::Subnormal,
693699
_ => FpCategory::Normal,
@@ -699,12 +705,9 @@ impl f32 {
699705
// plus a transmute. We do not live in a just world, but we can make it more so.
700706
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
701707
const fn classify_bits(b: u32) -> FpCategory {
702-
const EXP_MASK: u32 = 0x7f800000;
703-
const MAN_MASK: u32 = 0x007fffff;
704-
705-
match (b & MAN_MASK, b & EXP_MASK) {
706-
(0, EXP_MASK) => FpCategory::Infinite,
707-
(_, EXP_MASK) => FpCategory::Nan,
708+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
709+
(0, Self::EXP_MASK) => FpCategory::Infinite,
710+
(_, Self::EXP_MASK) => FpCategory::Nan,
708711
(0, 0) => FpCategory::Zero,
709712
(_, 0) => FpCategory::Subnormal,
710713
_ => FpCategory::Normal,
@@ -790,14 +793,13 @@ impl f32 {
790793
// We must use strictly integer arithmetic to prevent denormals from
791794
// flushing to zero after an arithmetic operation on some platforms.
792795
const TINY_BITS: u32 = 0x1; // Smallest positive f32.
793-
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
794796

795797
let bits = self.to_bits();
796798
if self.is_nan() || bits == Self::INFINITY.to_bits() {
797799
return self;
798800
}
799801

800-
let abs = bits & CLEAR_SIGN_MASK;
802+
let abs = bits & !Self::SIGN_MASK;
801803
let next_bits = if abs == 0 {
802804
TINY_BITS
803805
} else if bits == abs {
@@ -840,14 +842,13 @@ impl f32 {
840842
// We must use strictly integer arithmetic to prevent denormals from
841843
// flushing to zero after an arithmetic operation on some platforms.
842844
const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32.
843-
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
844845

845846
let bits = self.to_bits();
846847
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
847848
return self;
848849
}
849850

850-
let abs = bits & CLEAR_SIGN_MASK;
851+
let abs = bits & !Self::SIGN_MASK;
851852
let next_bits = if abs == 0 {
852853
NEG_TINY_BITS
853854
} else if bits == abs {

library/core/src/num/f64.rs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,15 @@ impl f64 {
489489
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
490490
pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64;
491491

492+
/// Sign bit
493+
const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
494+
495+
/// Exponent mask
496+
const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
497+
498+
/// Mantissa mask
499+
const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff;
500+
492501
/// Returns `true` if this value is NaN.
493502
///
494503
/// ```
@@ -514,9 +523,7 @@ impl f64 {
514523
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
515524
pub(crate) const fn abs_private(self) -> f64 {
516525
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
517-
unsafe {
518-
mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & 0x7fff_ffff_ffff_ffff)
519-
}
526+
unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & !Self::SIGN_MASK) }
520527
}
521528

522529
/// Returns `true` if this value is positive infinity or negative infinity, and
@@ -673,13 +680,10 @@ impl f64 {
673680
// and some normal floating point numbers truncated from an x87 FPU.
674681
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
675682
const unsafe fn partial_classify(self) -> FpCategory {
676-
const EXP_MASK: u64 = 0x7ff0000000000000;
677-
const MAN_MASK: u64 = 0x000fffffffffffff;
678-
679683
// SAFETY: The caller is not asking questions for which this will tell lies.
680684
let b = unsafe { mem::transmute::<f64, u64>(self) };
681-
match (b & MAN_MASK, b & EXP_MASK) {
682-
(0, EXP_MASK) => FpCategory::Infinite,
685+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
686+
(0, Self::EXP_MASK) => FpCategory::Infinite,
683687
(0, 0) => FpCategory::Zero,
684688
(_, 0) => FpCategory::Subnormal,
685689
_ => FpCategory::Normal,
@@ -691,12 +695,9 @@ impl f64 {
691695
// plus a transmute. We do not live in a just world, but we can make it more so.
692696
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
693697
const fn classify_bits(b: u64) -> FpCategory {
694-
const EXP_MASK: u64 = 0x7ff0000000000000;
695-
const MAN_MASK: u64 = 0x000fffffffffffff;
696-
697-
match (b & MAN_MASK, b & EXP_MASK) {
698-
(0, EXP_MASK) => FpCategory::Infinite,
699-
(_, EXP_MASK) => FpCategory::Nan,
698+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
699+
(0, Self::EXP_MASK) => FpCategory::Infinite,
700+
(_, Self::EXP_MASK) => FpCategory::Nan,
700701
(0, 0) => FpCategory::Zero,
701702
(_, 0) => FpCategory::Subnormal,
702703
_ => FpCategory::Normal,
@@ -756,7 +757,7 @@ impl f64 {
756757
// IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
757758
// applies to zeros and NaNs as well.
758759
// SAFETY: This is just transmuting to get the sign bit, it's fine.
759-
unsafe { mem::transmute::<f64, u64>(self) & 0x8000_0000_0000_0000 != 0 }
760+
unsafe { mem::transmute::<f64, u64>(self) & Self::SIGN_MASK != 0 }
760761
}
761762

762763
#[must_use]
@@ -800,14 +801,13 @@ impl f64 {
800801
// We must use strictly integer arithmetic to prevent denormals from
801802
// flushing to zero after an arithmetic operation on some platforms.
802803
const TINY_BITS: u64 = 0x1; // Smallest positive f64.
803-
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
804804

805805
let bits = self.to_bits();
806806
if self.is_nan() || bits == Self::INFINITY.to_bits() {
807807
return self;
808808
}
809809

810-
let abs = bits & CLEAR_SIGN_MASK;
810+
let abs = bits & !Self::SIGN_MASK;
811811
let next_bits = if abs == 0 {
812812
TINY_BITS
813813
} else if bits == abs {
@@ -850,14 +850,13 @@ impl f64 {
850850
// We must use strictly integer arithmetic to prevent denormals from
851851
// flushing to zero after an arithmetic operation on some platforms.
852852
const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
853-
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
854853

855854
let bits = self.to_bits();
856855
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
857856
return self;
858857
}
859858

860-
let abs = bits & CLEAR_SIGN_MASK;
859+
let abs = bits & !Self::SIGN_MASK;
861860
let next_bits = if abs == 0 {
862861
NEG_TINY_BITS
863862
} else if bits == abs {

library/std/src/f32/tests.rs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@ use crate::f32::consts;
22
use crate::num::FpCategory as Fp;
33
use crate::num::*;
44

5+
/// Smallest number
6+
const TINY_BITS: u32 = 0x1;
7+
/// Next smallest number
8+
const TINY_UP_BITS: u32 = 0x2;
9+
/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
10+
const MAX_DOWN_BITS: u32 = 0x7f7f_fffe;
11+
/// Zeroed exponent, full significant
12+
const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff;
13+
/// Exponent = 0b1, zeroed significand
14+
const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000;
15+
/// First pattern over the mantissa
16+
const NAN_MASK1: u32 = 0x002a_aaaa;
17+
/// Second pattern over the mantissa
18+
const NAN_MASK2: u32 = 0x0055_5555;
19+
20+
#[allow(unused_macros)]
21+
macro_rules! assert_f32_biteq {
22+
($left : expr, $right : expr) => {
23+
let l: &f32 = &$left;
24+
let r: &f32 = &$right;
25+
let lb = l.to_bits();
26+
let rb = r.to_bits();
27+
assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})");
28+
};
29+
}
30+
531
#[test]
632
fn test_num_f32() {
733
test_num(10f32, 2f32);
@@ -315,27 +341,16 @@ fn test_is_sign_negative() {
315341
assert!((-f32::NAN).is_sign_negative());
316342
}
317343

318-
#[allow(unused_macros)]
319-
macro_rules! assert_f32_biteq {
320-
($left : expr, $right : expr) => {
321-
let l: &f32 = &$left;
322-
let r: &f32 = &$right;
323-
let lb = l.to_bits();
324-
let rb = r.to_bits();
325-
assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
326-
};
327-
}
328-
329344
// Ignore test on x87 floating point, these platforms do not guarantee NaN
330345
// payloads are preserved and flush denormals to zero, failing the tests.
331346
#[cfg(not(target_arch = "x86"))]
332347
#[test]
333348
fn test_next_up() {
334-
let tiny = f32::from_bits(1);
335-
let tiny_up = f32::from_bits(2);
336-
let max_down = f32::from_bits(0x7f7f_fffe);
337-
let largest_subnormal = f32::from_bits(0x007f_ffff);
338-
let smallest_normal = f32::from_bits(0x0080_0000);
349+
let tiny = f32::from_bits(TINY_BITS);
350+
let tiny_up = f32::from_bits(TINY_UP_BITS);
351+
let max_down = f32::from_bits(MAX_DOWN_BITS);
352+
let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
353+
let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
339354
assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN);
340355
assert_f32_biteq!(f32::MIN.next_up(), -max_down);
341356
assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0);
@@ -352,8 +367,8 @@ fn test_next_up() {
352367

353368
// Check that NaNs roundtrip.
354369
let nan0 = f32::NAN;
355-
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
356-
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
370+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
371+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
357372
assert_f32_biteq!(nan0.next_up(), nan0);
358373
assert_f32_biteq!(nan1.next_up(), nan1);
359374
assert_f32_biteq!(nan2.next_up(), nan2);
@@ -364,11 +379,11 @@ fn test_next_up() {
364379
#[cfg(not(target_arch = "x86"))]
365380
#[test]
366381
fn test_next_down() {
367-
let tiny = f32::from_bits(1);
368-
let tiny_up = f32::from_bits(2);
369-
let max_down = f32::from_bits(0x7f7f_fffe);
370-
let largest_subnormal = f32::from_bits(0x007f_ffff);
371-
let smallest_normal = f32::from_bits(0x0080_0000);
382+
let tiny = f32::from_bits(TINY_BITS);
383+
let tiny_up = f32::from_bits(TINY_UP_BITS);
384+
let max_down = f32::from_bits(MAX_DOWN_BITS);
385+
let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
386+
let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
372387
assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY);
373388
assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY);
374389
assert_f32_biteq!((-max_down).next_down(), f32::MIN);
@@ -386,8 +401,8 @@ fn test_next_down() {
386401

387402
// Check that NaNs roundtrip.
388403
let nan0 = f32::NAN;
389-
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
390-
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
404+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
405+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
391406
assert_f32_biteq!(nan0.next_down(), nan0);
392407
assert_f32_biteq!(nan1.next_down(), nan1);
393408
assert_f32_biteq!(nan2.next_down(), nan2);
@@ -734,8 +749,8 @@ fn test_float_bits_conv() {
734749

735750
// Check that NaNs roundtrip their bits regardless of signaling-ness
736751
// 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
737-
let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA;
738-
let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555;
752+
let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1;
753+
let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2;
739754
assert!(f32::from_bits(masked_nan1).is_nan());
740755
assert!(f32::from_bits(masked_nan2).is_nan());
741756

0 commit comments

Comments
 (0)