Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a8b311e

Browse files
committedJun 26, 2024
Auto merge of rust-lang#126608 - tgross35:f16-f128-library, r=Mark-Simulacrum
Add more constants, functions, and tests for `f16` and `f128` This adds everything that was in some way blocked on const eval, since rust-lang#126429 landed. There is a lot of `cfg(bootstrap)` since that is a fairly recent change. `f128` tests are disabled on everything except x86_64 and Linux aarch64, which are two platforms I know have "good" support for these types - meaning basic math symbols are available and LLVM doesn't hit selection crashes. `f16` tests are enabled on almost everything except for known LLVM crashes. Doctests are only enabled on x86_64. Tracking issue: rust-lang#116909
2 parents c14a130 + 19cfdb2 commit a8b311e

File tree

12 files changed

+2638
-148
lines changed

12 files changed

+2638
-148
lines changed
 

‎core/src/num/f128.rs

+672-2
Large diffs are not rendered by default.

‎core/src/num/f16.rs

+644-2
Large diffs are not rendered by default.

‎core/src/num/f32.rs

+30-25
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,
@@ -787,19 +796,17 @@ impl f32 {
787796
#[unstable(feature = "float_next_up_down", issue = "91399")]
788797
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
789798
pub const fn next_up(self) -> Self {
790-
// We must use strictly integer arithmetic to prevent denormals from
791-
// 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-
799+
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
800+
// denormals to zero. This is in general unsound and unsupported, but here
801+
// we do our best to still produce the correct result on such targets.
795802
let bits = self.to_bits();
796803
if self.is_nan() || bits == Self::INFINITY.to_bits() {
797804
return self;
798805
}
799806

800-
let abs = bits & CLEAR_SIGN_MASK;
807+
let abs = bits & !Self::SIGN_MASK;
801808
let next_bits = if abs == 0 {
802-
TINY_BITS
809+
Self::TINY_BITS
803810
} else if bits == abs {
804811
bits + 1
805812
} else {
@@ -837,19 +844,17 @@ impl f32 {
837844
#[unstable(feature = "float_next_up_down", issue = "91399")]
838845
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
839846
pub const fn next_down(self) -> Self {
840-
// We must use strictly integer arithmetic to prevent denormals from
841-
// 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-
847+
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
848+
// denormals to zero. This is in general unsound and unsupported, but here
849+
// we do our best to still produce the correct result on such targets.
845850
let bits = self.to_bits();
846851
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
847852
return self;
848853
}
849854

850-
let abs = bits & CLEAR_SIGN_MASK;
855+
let abs = bits & !Self::SIGN_MASK;
851856
let next_bits = if abs == 0 {
852-
NEG_TINY_BITS
857+
Self::NEG_TINY_BITS
853858
} else if bits == abs {
854859
bits - 1
855860
} else {

‎core/src/num/f64.rs

+32-29
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]
@@ -797,19 +804,17 @@ impl f64 {
797804
#[unstable(feature = "float_next_up_down", issue = "91399")]
798805
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
799806
pub const fn next_up(self) -> Self {
800-
// We must use strictly integer arithmetic to prevent denormals from
801-
// 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-
807+
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
808+
// denormals to zero. This is in general unsound and unsupported, but here
809+
// we do our best to still produce the correct result on such targets.
805810
let bits = self.to_bits();
806811
if self.is_nan() || bits == Self::INFINITY.to_bits() {
807812
return self;
808813
}
809814

810-
let abs = bits & CLEAR_SIGN_MASK;
815+
let abs = bits & !Self::SIGN_MASK;
811816
let next_bits = if abs == 0 {
812-
TINY_BITS
817+
Self::TINY_BITS
813818
} else if bits == abs {
814819
bits + 1
815820
} else {
@@ -847,19 +852,17 @@ impl f64 {
847852
#[unstable(feature = "float_next_up_down", issue = "91399")]
848853
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
849854
pub const fn next_down(self) -> Self {
850-
// We must use strictly integer arithmetic to prevent denormals from
851-
// 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-
855+
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
856+
// denormals to zero. This is in general unsound and unsupported, but here
857+
// we do our best to still produce the correct result on such targets.
855858
let bits = self.to_bits();
856859
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
857860
return self;
858861
}
859862

860-
let abs = bits & CLEAR_SIGN_MASK;
863+
let abs = bits & !Self::SIGN_MASK;
861864
let next_bits = if abs == 0 {
862-
NEG_TINY_BITS
865+
Self::NEG_TINY_BITS
863866
} else if bits == abs {
864867
bits - 1
865868
} else {

‎std/build.rs

+62
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ fn main() {
77
let target_vendor =
88
env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set");
99
let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set");
10+
let target_pointer_width: u32 = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
11+
.expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set")
12+
.parse()
13+
.unwrap();
1014

1115
println!("cargo:rustc-check-cfg=cfg(netbsd10)");
1216
if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() {
@@ -70,4 +74,62 @@ fn main() {
7074
println!("cargo:rustc-cfg=backtrace_in_libstd");
7175

7276
println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap());
77+
78+
// Emit these on platforms that have no known ABI bugs, LLVM selection bugs, lowering bugs,
79+
// missing symbols, or other problems, to determine when tests get run.
80+
// If more broken platforms are found, please update the tracking issue at
81+
// <https://github.com/rust-lang/rust/issues/116909>
82+
//
83+
// Some of these match arms are redundant; the goal is to separate reasons that the type is
84+
// unreliable, even when multiple reasons might fail the same platform.
85+
println!("cargo:rustc-check-cfg=cfg(reliable_f16)");
86+
println!("cargo:rustc-check-cfg=cfg(reliable_f128)");
87+
88+
let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) {
89+
// Selection failure until recent LLVM <https://github.com/llvm/llvm-project/issues/93894>
90+
// FIXME(llvm19): can probably be removed at the version bump
91+
("loongarch64", _) => false,
92+
// Selection failure <https://github.com/llvm/llvm-project/issues/50374>
93+
("s390x", _) => false,
94+
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
95+
("arm64ec", _) => false,
96+
// MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
97+
("x86", "windows") => false,
98+
// x86 has ABI bugs that show up with optimizations. This should be partially fixed with
99+
// the compiler-builtins update. <https://github.com/rust-lang/rust/issues/123885>
100+
("x86" | "x86_64", _) => false,
101+
// Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee`
102+
("powerpc" | "powerpc64" | "powerpc64le", _) => false,
103+
// Missing `__extendhfsf` and `__truncsfhf`
104+
("riscv32" | "riscv64", _) => false,
105+
// Most OSs are missing `__extendhfsf` and `__truncsfhf`
106+
(_, "linux" | "macos") => true,
107+
// Almost all OSs besides Linux and MacOS are missing symbols until compiler-builtins can
108+
// be updated. <https://github.com/rust-lang/rust/pull/125016> will get some of these, the
109+
// next CB update should get the rest.
110+
_ => false,
111+
};
112+
113+
let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) {
114+
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
115+
("arm64ec", _) => false,
116+
// ABI and precision bugs <https://github.com/rust-lang/rust/issues/125109>
117+
// <https://github.com/rust-lang/rust/issues/125102>
118+
("powerpc" | "powerpc64", _) => false,
119+
// Selection bug <https://github.com/llvm/llvm-project/issues/95471>
120+
("nvptx64", _) => false,
121+
// ABI unsupported <https://github.com/llvm/llvm-project/issues/41838>
122+
("sparc", _) => false,
123+
// 64-bit Linux is about the only platform to have f128 symbols by default
124+
(_, "linux") if target_pointer_width == 64 => true,
125+
// Same as for f16, except MacOS is also missing f128 symbols.
126+
_ => false,
127+
};
128+
129+
if has_reliable_f16 {
130+
println!("cargo:rustc-cfg=reliable_f16");
131+
}
132+
if has_reliable_f128 {
133+
println!("cargo:rustc-cfg=reliable_f128");
134+
}
73135
}

‎std/src/f128.rs

+30
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,34 @@ impl f128 {
3232
pub fn powi(self, n: i32) -> f128 {
3333
unsafe { intrinsics::powif128(self, n) }
3434
}
35+
36+
/// Computes the absolute value of `self`.
37+
///
38+
/// This function always returns the precise result.
39+
///
40+
/// # Examples
41+
///
42+
/// ```
43+
/// #![feature(f128)]
44+
/// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128
45+
///
46+
/// let x = 3.5_f128;
47+
/// let y = -3.5_f128;
48+
///
49+
/// assert_eq!(x.abs(), x);
50+
/// assert_eq!(y.abs(), -y);
51+
///
52+
/// assert!(f128::NAN.abs().is_nan());
53+
/// # }
54+
/// ```
55+
#[inline]
56+
#[cfg(not(bootstrap))]
57+
#[rustc_allow_incoherent_impl]
58+
#[unstable(feature = "f128", issue = "116909")]
59+
#[must_use = "method returns a new number and does not mutate the original value"]
60+
pub fn abs(self) -> Self {
61+
// FIXME(f16_f128): replace with `intrinsics::fabsf128` when available
62+
// We don't do this now because LLVM has lowering bugs for f128 math.
63+
Self::from_bits(self.to_bits() & !(1 << 127))
64+
}
3565
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.