Skip to content

Commit 6839ec5

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 12ec5b7 commit 6839ec5

File tree

4 files changed

+160
-101
lines changed

4 files changed

+160
-101
lines changed

core/src/num/f32.rs

+24-21
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,21 @@ 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+
502+
/// Minimum representable positive value (min subnormal)
503+
const TINY_BITS: u32 = 0x1;
504+
505+
/// Minimum representable negative value (min negative subnormal)
506+
const NEG_TINY_BITS: u32 = Self::TINY_BITS | Self::SIGN_MASK;
507+
493508
/// Returns `true` if this value is NaN.
494509
///
495510
/// ```
@@ -515,7 +530,7 @@ impl f32 {
515530
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
516531
pub(crate) const fn abs_private(self) -> f32 {
517532
// 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) }
533+
unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & !Self::SIGN_MASK) }
519534
}
520535

521536
/// Returns `true` if this value is positive infinity or negative infinity, and
@@ -682,12 +697,9 @@ impl f32 {
682697
// runtime-deviating logic which may or may not be acceptable.
683698
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
684699
const unsafe fn partial_classify(self) -> FpCategory {
685-
const EXP_MASK: u32 = 0x7f800000;
686-
const MAN_MASK: u32 = 0x007fffff;
687-
688700
// SAFETY: The caller is not asking questions for which this will tell lies.
689701
let b = unsafe { mem::transmute::<f32, u32>(self) };
690-
match (b & MAN_MASK, b & EXP_MASK) {
702+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
691703
(0, 0) => FpCategory::Zero,
692704
(_, 0) => FpCategory::Subnormal,
693705
_ => FpCategory::Normal,
@@ -699,12 +711,9 @@ impl f32 {
699711
// plus a transmute. We do not live in a just world, but we can make it more so.
700712
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
701713
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,
714+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
715+
(0, Self::EXP_MASK) => FpCategory::Infinite,
716+
(_, Self::EXP_MASK) => FpCategory::Nan,
708717
(0, 0) => FpCategory::Zero,
709718
(_, 0) => FpCategory::Subnormal,
710719
_ => FpCategory::Normal,
@@ -789,17 +798,14 @@ impl f32 {
789798
pub const fn next_up(self) -> Self {
790799
// We must use strictly integer arithmetic to prevent denormals from
791800
// flushing to zero after an arithmetic operation on some platforms.
792-
const TINY_BITS: u32 = 0x1; // Smallest positive f32.
793-
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
794-
795801
let bits = self.to_bits();
796802
if self.is_nan() || bits == Self::INFINITY.to_bits() {
797803
return self;
798804
}
799805

800-
let abs = bits & CLEAR_SIGN_MASK;
806+
let abs = bits & !Self::SIGN_MASK;
801807
let next_bits = if abs == 0 {
802-
TINY_BITS
808+
Self::TINY_BITS
803809
} else if bits == abs {
804810
bits + 1
805811
} else {
@@ -839,17 +845,14 @@ impl f32 {
839845
pub const fn next_down(self) -> Self {
840846
// We must use strictly integer arithmetic to prevent denormals from
841847
// flushing to zero after an arithmetic operation on some platforms.
842-
const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32.
843-
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
844-
845848
let bits = self.to_bits();
846849
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
847850
return self;
848851
}
849852

850-
let abs = bits & CLEAR_SIGN_MASK;
853+
let abs = bits & !Self::SIGN_MASK;
851854
let next_bits = if abs == 0 {
852-
NEG_TINY_BITS
855+
Self::NEG_TINY_BITS
853856
} else if bits == abs {
854857
bits - 1
855858
} else {

core/src/num/f64.rs

+26-25
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,21 @@ 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+
501+
/// Minimum representable positive value (min subnormal)
502+
const TINY_BITS: u64 = 0x1;
503+
504+
/// Minimum representable negative value (min negative subnormal)
505+
const NEG_TINY_BITS: u64 = Self::TINY_BITS | Self::SIGN_MASK;
506+
492507
/// Returns `true` if this value is NaN.
493508
///
494509
/// ```
@@ -514,9 +529,7 @@ impl f64 {
514529
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
515530
pub(crate) const fn abs_private(self) -> f64 {
516531
// 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-
}
532+
unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & !Self::SIGN_MASK) }
520533
}
521534

522535
/// Returns `true` if this value is positive infinity or negative infinity, and
@@ -673,13 +686,10 @@ impl f64 {
673686
// and some normal floating point numbers truncated from an x87 FPU.
674687
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
675688
const unsafe fn partial_classify(self) -> FpCategory {
676-
const EXP_MASK: u64 = 0x7ff0000000000000;
677-
const MAN_MASK: u64 = 0x000fffffffffffff;
678-
679689
// SAFETY: The caller is not asking questions for which this will tell lies.
680690
let b = unsafe { mem::transmute::<f64, u64>(self) };
681-
match (b & MAN_MASK, b & EXP_MASK) {
682-
(0, EXP_MASK) => FpCategory::Infinite,
691+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
692+
(0, Self::EXP_MASK) => FpCategory::Infinite,
683693
(0, 0) => FpCategory::Zero,
684694
(_, 0) => FpCategory::Subnormal,
685695
_ => FpCategory::Normal,
@@ -691,12 +701,9 @@ impl f64 {
691701
// plus a transmute. We do not live in a just world, but we can make it more so.
692702
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
693703
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,
704+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
705+
(0, Self::EXP_MASK) => FpCategory::Infinite,
706+
(_, Self::EXP_MASK) => FpCategory::Nan,
700707
(0, 0) => FpCategory::Zero,
701708
(_, 0) => FpCategory::Subnormal,
702709
_ => FpCategory::Normal,
@@ -756,7 +763,7 @@ impl f64 {
756763
// IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
757764
// applies to zeros and NaNs as well.
758765
// 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 }
766+
unsafe { mem::transmute::<f64, u64>(self) & Self::SIGN_MASK != 0 }
760767
}
761768

762769
#[must_use]
@@ -799,17 +806,14 @@ impl f64 {
799806
pub const fn next_up(self) -> Self {
800807
// We must use strictly integer arithmetic to prevent denormals from
801808
// flushing to zero after an arithmetic operation on some platforms.
802-
const TINY_BITS: u64 = 0x1; // Smallest positive f64.
803-
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
804-
805809
let bits = self.to_bits();
806810
if self.is_nan() || bits == Self::INFINITY.to_bits() {
807811
return self;
808812
}
809813

810-
let abs = bits & CLEAR_SIGN_MASK;
814+
let abs = bits & !Self::SIGN_MASK;
811815
let next_bits = if abs == 0 {
812-
TINY_BITS
816+
Self::TINY_BITS
813817
} else if bits == abs {
814818
bits + 1
815819
} else {
@@ -849,17 +853,14 @@ impl f64 {
849853
pub const fn next_down(self) -> Self {
850854
// We must use strictly integer arithmetic to prevent denormals from
851855
// flushing to zero after an arithmetic operation on some platforms.
852-
const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
853-
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
854-
855856
let bits = self.to_bits();
856857
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
857858
return self;
858859
}
859860

860-
let abs = bits & CLEAR_SIGN_MASK;
861+
let abs = bits & !Self::SIGN_MASK;
861862
let next_bits = if abs == 0 {
862-
NEG_TINY_BITS
863+
Self::NEG_TINY_BITS
863864
} else if bits == abs {
864865
bits - 1
865866
} else {

std/src/f32/tests.rs

+55-27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,45 @@ use crate::f32::consts;
22
use crate::num::FpCategory as Fp;
33
use crate::num::*;
44

5+
/// Smallest number
6+
#[allow(dead_code)] // unused on x86
7+
const TINY_BITS: u32 = 0x1;
8+
9+
/// Next smallest number
10+
#[allow(dead_code)] // unused on x86
11+
const TINY_UP_BITS: u32 = 0x2;
12+
13+
/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
14+
#[allow(dead_code)] // unused on x86
15+
const MAX_DOWN_BITS: u32 = 0x7f7f_fffe;
16+
17+
/// Zeroed exponent, full significant
18+
#[allow(dead_code)] // unused on x86
19+
const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff;
20+
21+
/// Exponent = 0b1, zeroed significand
22+
#[allow(dead_code)] // unused on x86
23+
const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000;
24+
25+
/// First pattern over the mantissa
26+
#[allow(dead_code)] // unused on x86
27+
const NAN_MASK1: u32 = 0x002a_aaaa;
28+
29+
/// Second pattern over the mantissa
30+
#[allow(dead_code)] // unused on x86
31+
const NAN_MASK2: u32 = 0x0055_5555;
32+
33+
#[allow(unused_macros)]
34+
macro_rules! assert_f32_biteq {
35+
($left : expr, $right : expr) => {
36+
let l: &f32 = &$left;
37+
let r: &f32 = &$right;
38+
let lb = l.to_bits();
39+
let rb = r.to_bits();
40+
assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})");
41+
};
42+
}
43+
544
#[test]
645
fn test_num_f32() {
746
test_num(10f32, 2f32);
@@ -315,27 +354,16 @@ fn test_is_sign_negative() {
315354
assert!((-f32::NAN).is_sign_negative());
316355
}
317356

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-
329357
// Ignore test on x87 floating point, these platforms do not guarantee NaN
330358
// payloads are preserved and flush denormals to zero, failing the tests.
331359
#[cfg(not(target_arch = "x86"))]
332360
#[test]
333361
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);
362+
let tiny = f32::from_bits(TINY_BITS);
363+
let tiny_up = f32::from_bits(TINY_UP_BITS);
364+
let max_down = f32::from_bits(MAX_DOWN_BITS);
365+
let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
366+
let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
339367
assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN);
340368
assert_f32_biteq!(f32::MIN.next_up(), -max_down);
341369
assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0);
@@ -352,8 +380,8 @@ fn test_next_up() {
352380

353381
// Check that NaNs roundtrip.
354382
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);
383+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
384+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
357385
assert_f32_biteq!(nan0.next_up(), nan0);
358386
assert_f32_biteq!(nan1.next_up(), nan1);
359387
assert_f32_biteq!(nan2.next_up(), nan2);
@@ -364,11 +392,11 @@ fn test_next_up() {
364392
#[cfg(not(target_arch = "x86"))]
365393
#[test]
366394
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);
395+
let tiny = f32::from_bits(TINY_BITS);
396+
let tiny_up = f32::from_bits(TINY_UP_BITS);
397+
let max_down = f32::from_bits(MAX_DOWN_BITS);
398+
let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
399+
let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
372400
assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY);
373401
assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY);
374402
assert_f32_biteq!((-max_down).next_down(), f32::MIN);
@@ -386,8 +414,8 @@ fn test_next_down() {
386414

387415
// Check that NaNs roundtrip.
388416
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);
417+
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
418+
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
391419
assert_f32_biteq!(nan0.next_down(), nan0);
392420
assert_f32_biteq!(nan1.next_down(), nan1);
393421
assert_f32_biteq!(nan2.next_down(), nan2);
@@ -734,8 +762,8 @@ fn test_float_bits_conv() {
734762

735763
// Check that NaNs roundtrip their bits regardless of signaling-ness
736764
// 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;
765+
let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1;
766+
let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2;
739767
assert!(f32::from_bits(masked_nan1).is_nan());
740768
assert!(f32::from_bits(masked_nan2).is_nan());
741769

0 commit comments

Comments
 (0)