From 180eacea1c51610f6ddd550dc0dac5e33d313030 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 31 Aug 2024 22:18:30 +0200 Subject: [PATCH 1/4] these tests seem to work fine on i586 these days --- library/std/src/f32/tests.rs | 13 ------------- library/std/src/f64/tests.rs | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 3a4c1c120a495..99cfcfb231dad 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -2,31 +2,24 @@ use crate::f32::consts; use crate::num::{FpCategory as Fp, *}; /// Smallest number -#[allow(dead_code)] // unused on x86 const TINY_BITS: u32 = 0x1; /// Next smallest number -#[allow(dead_code)] // unused on x86 const TINY_UP_BITS: u32 = 0x2; /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -#[allow(dead_code)] // unused on x86 const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; /// Zeroed exponent, full significant -#[allow(dead_code)] // unused on x86 const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; /// Exponent = 0b1, zeroed significand -#[allow(dead_code)] // unused on x86 const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; /// First pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK1: u32 = 0x002a_aaaa; /// Second pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK2: u32 = 0x0055_5555; #[allow(unused_macros)] @@ -353,9 +346,6 @@ fn test_is_sign_negative() { assert!((-f32::NAN).is_sign_negative()); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { let tiny = f32::from_bits(TINY_BITS); @@ -386,9 +376,6 @@ fn test_next_up() { assert_f32_biteq!(nan2.next_up(), nan2); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { let tiny = f32::from_bits(TINY_BITS); diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index bac8405f97361..3fac2efe0d76c 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -2,31 +2,24 @@ use crate::f64::consts; use crate::num::{FpCategory as Fp, *}; /// Smallest number -#[allow(dead_code)] // unused on x86 const TINY_BITS: u64 = 0x1; /// Next smallest number -#[allow(dead_code)] // unused on x86 const TINY_UP_BITS: u64 = 0x2; /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -#[allow(dead_code)] // unused on x86 const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; /// Zeroed exponent, full significant -#[allow(dead_code)] // unused on x86 const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; /// Exponent = 0b1, zeroed significand -#[allow(dead_code)] // unused on x86 const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; /// First pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK2: u64 = 0x0005_5555_5555_5555; #[allow(unused_macros)] @@ -343,9 +336,6 @@ fn test_is_sign_negative() { assert!((-f64::NAN).is_sign_negative()); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { let tiny = f64::from_bits(TINY_BITS); @@ -375,9 +365,6 @@ fn test_next_up() { assert_f64_biteq!(nan2.next_up(), nan2); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { let tiny = f64::from_bits(TINY_BITS); From c40ee79b849daee16f3ab07a5e2bae409d40aca1 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 10 Sep 2024 16:05:37 -0700 Subject: [PATCH 2/4] move float tests into their own dir --- .../const-float-classify.rs => float/classify-runtime-const.rs} | 0 .../classify-runtime-const.stderr} | 0 .../const-float-bits-conv.rs => float/conv-bits-runtime-const.rs} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{consts/const-float-classify.rs => float/classify-runtime-const.rs} (100%) rename tests/ui/{consts/const-float-classify.stderr => float/classify-runtime-const.stderr} (100%) rename tests/ui/{consts/const-float-bits-conv.rs => float/conv-bits-runtime-const.rs} (100%) diff --git a/tests/ui/consts/const-float-classify.rs b/tests/ui/float/classify-runtime-const.rs similarity index 100% rename from tests/ui/consts/const-float-classify.rs rename to tests/ui/float/classify-runtime-const.rs diff --git a/tests/ui/consts/const-float-classify.stderr b/tests/ui/float/classify-runtime-const.stderr similarity index 100% rename from tests/ui/consts/const-float-classify.stderr rename to tests/ui/float/classify-runtime-const.stderr diff --git a/tests/ui/consts/const-float-bits-conv.rs b/tests/ui/float/conv-bits-runtime-const.rs similarity index 100% rename from tests/ui/consts/const-float-bits-conv.rs rename to tests/ui/float/conv-bits-runtime-const.rs From 3daa9518d5234d0768ba8ea81fd19c372ab98445 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 31 Aug 2024 22:23:13 +0200 Subject: [PATCH 3/4] enable and extend float-classify test --- tests/ui/float/classify-runtime-const.rs | 74 ++++---- tests/ui/float/classify-runtime-const.stderr | 11 -- tests/ui/float/conv-bits-runtime-const.rs | 169 ++++++++++--------- 3 files changed, 128 insertions(+), 126 deletions(-) delete mode 100644 tests/ui/float/classify-runtime-const.stderr diff --git a/tests/ui/float/classify-runtime-const.rs b/tests/ui/float/classify-runtime-const.rs index 6e5097f7f2b9c..59a232c255e8d 100644 --- a/tests/ui/float/classify-runtime-const.rs +++ b/tests/ui/float/classify-runtime-const.rs @@ -1,40 +1,46 @@ //@ compile-flags: -Zmir-opt-level=0 -Znext-solver -//@ known-bug: #110395 -// FIXME(effects) run-pass +//@ run-pass +// ignore-tidy-linelength +// This tests the float classification functions, for regular runtime code and for const evaluation. + +#![feature(f16_const)] +#![feature(f128_const)] #![feature(const_float_classify)] -#![feature(const_trait_impl, effects)] -#![allow(incomplete_features)] -// Don't promote -const fn nop(x: T) -> T { x } +use std::hint::black_box; +use std::num::FpCategory::*; -impl const PartialEq for bool { - fn eq(&self, _: &NonDet) -> bool { - true - } -} - -macro_rules! const_assert { - ($a:expr, $b:expr) => { +macro_rules! both_assert { + ($a:expr, NonDet) => { { - const _: () = assert!($a == $b); - assert!(nop($a) == nop($b)); + // Compute `a`, but do not compare with anything as the result is non-deterministic. + const _: () = { let _val = $a; }; + // `black_box` prevents promotion, and MIR opts are disabled above, so this is truly + // going through LLVM. + let _val = black_box($a); + } + }; + ($a:expr, $b:ident) => { + { + const _: () = assert!(matches!($a, $b)); + assert!(black_box($a) == black_box($b)); } }; } macro_rules! suite { - ( $( $tt:tt )* ) => { + ( $tyname:ident: $( $tt:tt )* ) => { fn f32() { + type $tyname = f32; suite_inner!(f32 $($tt)*); } fn f64() { + type $tyname = f64; suite_inner!(f64 $($tt)*); } } - } macro_rules! suite_inner { @@ -44,33 +50,33 @@ macro_rules! suite_inner { $( $tail:tt )* ) => { - $( const_assert!($ty::$fn($val), $out); )* + $( both_assert!($ty::$fn($val), $out); )* suite_inner!($ty [$($fn),*] $($tail)*) }; ( $ty:ident [$( $fn:ident ),*]) => {}; } -#[derive(Debug)] -struct NonDet; - -// The result of the `is_sign` methods are not checked for correctness, since LLVM does not +// The result of the `is_sign` methods are not checked for correctness, since we do not // guarantee anything about the signedness of NaNs. See -// https://github.com/rust-lang/rust/issues/55131. +// https://rust-lang.github.io/rfcs/3514-float-semantics.html. -suite! { - [is_nan, is_infinite, is_finite, is_normal, is_sign_positive, is_sign_negative] - -0.0 / 0.0 => [ true, false, false, false, NonDet, NonDet] - 0.0 / 0.0 => [ true, false, false, false, NonDet, NonDet] - 1.0 => [ false, false, true, true, true, false] - -1.0 => [ false, false, true, true, false, true] - 0.0 => [ false, false, true, false, true, false] - -0.0 => [ false, false, true, false, false, true] - 1.0 / 0.0 => [ false, true, false, false, true, false] - -1.0 / 0.0 => [ false, true, false, false, false, true] +suite! { T: // type alias for the type we are testing + [ classify, is_nan, is_infinite, is_finite, is_normal, is_sign_positive, is_sign_negative] + -0.0 / 0.0 => [ Nan, true, false, false, false, NonDet, NonDet] + 0.0 / 0.0 => [ Nan, true, false, false, false, NonDet, NonDet] + 1.0 => [ Normal, false, false, true, true, true, false] + -1.0 => [ Normal, false, false, true, true, false, true] + 0.0 => [ Zero, false, false, true, false, true, false] + -0.0 => [ Zero, false, false, true, false, false, true] + 1.0 / 0.0 => [ Infinite, false, true, false, false, true, false] + -1.0 / 0.0 => [ Infinite, false, true, false, false, false, true] + 1.0 / T::MAX => [Subnormal, false, false, true, false, true, false] + -1.0 / T::MAX => [Subnormal, false, false, true, false, false, true] } fn main() { f32(); f64(); + // FIXME(f16_f128): also test f16 and f128 } diff --git a/tests/ui/float/classify-runtime-const.stderr b/tests/ui/float/classify-runtime-const.stderr deleted file mode 100644 index a35de8ad0eabd..0000000000000 --- a/tests/ui/float/classify-runtime-const.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]` - --> $DIR/const-float-classify.rs:12:12 - | -LL | impl const PartialEq for bool { - | ^^^^^^^^^^^^^^^^^ - | - = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` - = note: adding a non-const method body in the future would be a breaking change - -error: aborting due to 1 previous error - diff --git a/tests/ui/float/conv-bits-runtime-const.rs b/tests/ui/float/conv-bits-runtime-const.rs index 869498d107612..e85a889d2c240 100644 --- a/tests/ui/float/conv-bits-runtime-const.rs +++ b/tests/ui/float/conv-bits-runtime-const.rs @@ -1,49 +1,55 @@ //@ compile-flags: -Zmir-opt-level=0 //@ run-pass +// This tests the float classification functions, for regular runtime code and for const evaluation. + #![feature(const_float_classify)] -#![feature(f16, f16_const)] -#![feature(f128, f128_const)] +#![feature(f16)] +#![feature(f128)] +#![feature(f16_const)] +#![feature(f128_const)] #![allow(unused_macro_rules)] -// Don't promote -const fn nop(x: T) -> T { x } -macro_rules! const_assert { +use std::hint::black_box; + +macro_rules! both_assert { ($a:expr) => { { const _: () = assert!($a); - assert!(nop($a)); + // `black_box` prevents promotion, and MIR opts are disabled above, so this is truly + // going through LLVM. + assert!(black_box($a)); } }; ($a:expr, $b:expr) => { { const _: () = assert!($a == $b); - assert_eq!(nop($a), nop($b)); + assert_eq!(black_box($a), black_box($b)); } }; } fn has_broken_floats() -> bool { // i586 targets are broken due to . - std::env::var("TARGET").is_ok_and(|v| v.contains("i586")) + cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) } #[cfg(target_arch = "x86_64")] fn f16(){ - const_assert!((1f16).to_bits(), 0x3c00); - const_assert!(u16::from_be_bytes(1f16.to_be_bytes()), 0x3c00); - const_assert!((12.5f16).to_bits(), 0x4a40); - const_assert!(u16::from_le_bytes(12.5f16.to_le_bytes()), 0x4a40); - const_assert!((1337f16).to_bits(), 0x6539); - const_assert!(u16::from_ne_bytes(1337f16.to_ne_bytes()), 0x6539); - const_assert!((-14.25f16).to_bits(), 0xcb20); - const_assert!(f16::from_bits(0x3c00), 1.0); - const_assert!(f16::from_be_bytes(0x3c00u16.to_be_bytes()), 1.0); - const_assert!(f16::from_bits(0x4a40), 12.5); - const_assert!(f16::from_le_bytes(0x4a40u16.to_le_bytes()), 12.5); - const_assert!(f16::from_bits(0x5be0), 252.0); - const_assert!(f16::from_ne_bytes(0x5be0u16.to_ne_bytes()), 252.0); - const_assert!(f16::from_bits(0xcb20), -14.25); + both_assert!((1f16).to_bits(), 0x3c00); + both_assert!(u16::from_be_bytes(1f16.to_be_bytes()), 0x3c00); + both_assert!((12.5f16).to_bits(), 0x4a40); + both_assert!(u16::from_le_bytes(12.5f16.to_le_bytes()), 0x4a40); + both_assert!((1337f16).to_bits(), 0x6539); + both_assert!(u16::from_ne_bytes(1337f16.to_ne_bytes()), 0x6539); + both_assert!((-14.25f16).to_bits(), 0xcb20); + both_assert!(f16::from_bits(0x3c00), 1.0); + both_assert!(f16::from_be_bytes(0x3c00u16.to_be_bytes()), 1.0); + both_assert!(f16::from_bits(0x4a40), 12.5); + both_assert!(f16::from_le_bytes(0x4a40u16.to_le_bytes()), 12.5); + both_assert!(f16::from_bits(0x5be0), 252.0); + both_assert!(f16::from_ne_bytes(0x5be0u16.to_ne_bytes()), 252.0); + both_assert!(f16::from_bits(0xcb20), -14.25); // Check that NaNs roundtrip their bits regardless of signalingness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits @@ -51,29 +57,29 @@ fn f16(){ const QUIET_NAN: u16 = f16::NAN.to_bits() ^ 0x0155; const SIGNALING_NAN: u16 = f16::NAN.to_bits() ^ 0x02AA; - const_assert!(f16::from_bits(QUIET_NAN).is_nan()); - const_assert!(f16::from_bits(SIGNALING_NAN).is_nan()); - const_assert!(f16::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); + both_assert!(f16::from_bits(QUIET_NAN).is_nan()); + both_assert!(f16::from_bits(SIGNALING_NAN).is_nan()); + both_assert!(f16::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); if !has_broken_floats() { - const_assert!(f16::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); + both_assert!(f16::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); } } fn f32() { - const_assert!((1f32).to_bits(), 0x3f800000); - const_assert!(u32::from_be_bytes(1f32.to_be_bytes()), 0x3f800000); - const_assert!((12.5f32).to_bits(), 0x41480000); - const_assert!(u32::from_le_bytes(12.5f32.to_le_bytes()), 0x41480000); - const_assert!((1337f32).to_bits(), 0x44a72000); - const_assert!(u32::from_ne_bytes(1337f32.to_ne_bytes()), 0x44a72000); - const_assert!((-14.25f32).to_bits(), 0xc1640000); - const_assert!(f32::from_bits(0x3f800000), 1.0); - const_assert!(f32::from_be_bytes(0x3f800000u32.to_be_bytes()), 1.0); - const_assert!(f32::from_bits(0x41480000), 12.5); - const_assert!(f32::from_le_bytes(0x41480000u32.to_le_bytes()), 12.5); - const_assert!(f32::from_bits(0x44a72000), 1337.0); - const_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0); - const_assert!(f32::from_bits(0xc1640000), -14.25); + both_assert!((1f32).to_bits(), 0x3f800000); + both_assert!(u32::from_be_bytes(1f32.to_be_bytes()), 0x3f800000); + both_assert!((12.5f32).to_bits(), 0x41480000); + both_assert!(u32::from_le_bytes(12.5f32.to_le_bytes()), 0x41480000); + both_assert!((1337f32).to_bits(), 0x44a72000); + both_assert!(u32::from_ne_bytes(1337f32.to_ne_bytes()), 0x44a72000); + both_assert!((-14.25f32).to_bits(), 0xc1640000); + both_assert!(f32::from_bits(0x3f800000), 1.0); + both_assert!(f32::from_be_bytes(0x3f800000u32.to_be_bytes()), 1.0); + both_assert!(f32::from_bits(0x41480000), 12.5); + both_assert!(f32::from_le_bytes(0x41480000u32.to_le_bytes()), 12.5); + both_assert!(f32::from_bits(0x44a72000), 1337.0); + both_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0); + both_assert!(f32::from_bits(0xc1640000), -14.25); // Check that NaNs roundtrip their bits regardless of signalingness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits @@ -81,29 +87,29 @@ fn f32() { const QUIET_NAN: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA; const SIGNALING_NAN: u32 = f32::NAN.to_bits() ^ 0x0055_5555; - const_assert!(f32::from_bits(QUIET_NAN).is_nan()); - const_assert!(f32::from_bits(SIGNALING_NAN).is_nan()); - const_assert!(f32::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); + both_assert!(f32::from_bits(QUIET_NAN).is_nan()); + both_assert!(f32::from_bits(SIGNALING_NAN).is_nan()); + both_assert!(f32::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); if !has_broken_floats() { - const_assert!(f32::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); + both_assert!(f32::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); } } fn f64() { - const_assert!((1f64).to_bits(), 0x3ff0000000000000); - const_assert!(u64::from_be_bytes(1f64.to_be_bytes()), 0x3ff0000000000000); - const_assert!((12.5f64).to_bits(), 0x4029000000000000); - const_assert!(u64::from_le_bytes(12.5f64.to_le_bytes()), 0x4029000000000000); - const_assert!((1337f64).to_bits(), 0x4094e40000000000); - const_assert!(u64::from_ne_bytes(1337f64.to_ne_bytes()), 0x4094e40000000000); - const_assert!((-14.25f64).to_bits(), 0xc02c800000000000); - const_assert!(f64::from_bits(0x3ff0000000000000), 1.0); - const_assert!(f64::from_be_bytes(0x3ff0000000000000u64.to_be_bytes()), 1.0); - const_assert!(f64::from_bits(0x4029000000000000), 12.5); - const_assert!(f64::from_le_bytes(0x4029000000000000u64.to_le_bytes()), 12.5); - const_assert!(f64::from_bits(0x4094e40000000000), 1337.0); - const_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0); - const_assert!(f64::from_bits(0xc02c800000000000), -14.25); + both_assert!((1f64).to_bits(), 0x3ff0000000000000); + both_assert!(u64::from_be_bytes(1f64.to_be_bytes()), 0x3ff0000000000000); + both_assert!((12.5f64).to_bits(), 0x4029000000000000); + both_assert!(u64::from_le_bytes(12.5f64.to_le_bytes()), 0x4029000000000000); + both_assert!((1337f64).to_bits(), 0x4094e40000000000); + both_assert!(u64::from_ne_bytes(1337f64.to_ne_bytes()), 0x4094e40000000000); + both_assert!((-14.25f64).to_bits(), 0xc02c800000000000); + both_assert!(f64::from_bits(0x3ff0000000000000), 1.0); + both_assert!(f64::from_be_bytes(0x3ff0000000000000u64.to_be_bytes()), 1.0); + both_assert!(f64::from_bits(0x4029000000000000), 12.5); + both_assert!(f64::from_le_bytes(0x4029000000000000u64.to_le_bytes()), 12.5); + both_assert!(f64::from_bits(0x4094e40000000000), 1337.0); + both_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0); + both_assert!(f64::from_bits(0xc02c800000000000), -14.25); // Check that NaNs roundtrip their bits regardless of signalingness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits @@ -111,30 +117,30 @@ fn f64() { const QUIET_NAN: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; const SIGNALING_NAN: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; - const_assert!(f64::from_bits(QUIET_NAN).is_nan()); - const_assert!(f64::from_bits(SIGNALING_NAN).is_nan()); - const_assert!(f64::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); + both_assert!(f64::from_bits(QUIET_NAN).is_nan()); + both_assert!(f64::from_bits(SIGNALING_NAN).is_nan()); + both_assert!(f64::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); if !has_broken_floats() { - const_assert!(f64::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); + both_assert!(f64::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); } } #[cfg(target_arch = "x86_64")] fn f128() { - const_assert!((1f128).to_bits(), 0x3fff0000000000000000000000000000); - const_assert!(u128::from_be_bytes(1f128.to_be_bytes()), 0x3fff0000000000000000000000000000); - const_assert!((12.5f128).to_bits(), 0x40029000000000000000000000000000); - const_assert!(u128::from_le_bytes(12.5f128.to_le_bytes()), 0x40029000000000000000000000000000); - const_assert!((1337f128).to_bits(), 0x40094e40000000000000000000000000); - const_assert!(u128::from_ne_bytes(1337f128.to_ne_bytes()), 0x40094e40000000000000000000000000); - const_assert!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - const_assert!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - const_assert!(f128::from_be_bytes(0x3fff0000000000000000000000000000u128.to_be_bytes()), 1.0); - const_assert!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - const_assert!(f128::from_le_bytes(0x40029000000000000000000000000000u128.to_le_bytes()), 12.5); - const_assert!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); + both_assert!((1f128).to_bits(), 0x3fff0000000000000000000000000000); + both_assert!(u128::from_be_bytes(1f128.to_be_bytes()), 0x3fff0000000000000000000000000000); + both_assert!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + both_assert!(u128::from_le_bytes(12.5f128.to_le_bytes()), 0x40029000000000000000000000000000); + both_assert!((1337f128).to_bits(), 0x40094e40000000000000000000000000); + both_assert!(u128::from_ne_bytes(1337f128.to_ne_bytes()), 0x40094e40000000000000000000000000); + both_assert!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); + both_assert!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); + both_assert!(f128::from_be_bytes(0x3fff0000000000000000000000000000u128.to_be_bytes()), 1.0); + both_assert!(f128::from_bits(0x40029000000000000000000000000000), 12.5); + both_assert!(f128::from_le_bytes(0x40029000000000000000000000000000u128.to_le_bytes()), 12.5); + both_assert!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); assert_eq!(f128::from_ne_bytes(0x40094e40000000000000000000000000u128.to_ne_bytes()), 1337.0); - const_assert!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + both_assert!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); // Check that NaNs roundtrip their bits regardless of signalingness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits @@ -142,20 +148,21 @@ fn f128() { const QUIET_NAN: u128 = f128::NAN.to_bits() | 0x0000_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA; const SIGNALING_NAN: u128 = f128::NAN.to_bits() ^ 0x0000_5555_5555_5555_5555_5555_5555_5555; - const_assert!(f128::from_bits(QUIET_NAN).is_nan()); - const_assert!(f128::from_bits(SIGNALING_NAN).is_nan()); - const_assert!(f128::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); + both_assert!(f128::from_bits(QUIET_NAN).is_nan()); + both_assert!(f128::from_bits(SIGNALING_NAN).is_nan()); + both_assert!(f128::from_bits(QUIET_NAN).to_bits(), QUIET_NAN); if !has_broken_floats() { - const_assert!(f128::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); + both_assert!(f128::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN); } } fn main() { + f32(); + f64(); + #[cfg(target_arch = "x86_64")] { f16(); f128(); } - f32(); - f64(); } From e556c136f3a4e5ff36afa248a6dd0e2cc9ddaced Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 31 Aug 2024 22:28:17 +0200 Subject: [PATCH 4/4] clean up internal comments about float semantics - remove an outdated FIXME - add reference to floating-point semantics issue Co-authored-by: Jubilee --- library/core/src/num/f16.rs | 1 + library/core/src/num/f32.rs | 5 +---- library/core/src/num/f64.rs | 5 +---- tests/ui/numbers-arithmetic/issue-105626.rs | 1 + 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 9252e8c601558..da92da1086dab 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -435,6 +435,7 @@ impl f16 { // WASM, see llvm/llvm-project#96437). These are platforms bugs, and Rust will misbehave on // such platforms, but we can at least try to make things seem as sane as possible by being // careful here. + // see also https://github.com/rust-lang/rust/issues/114479 if self.is_infinite() { // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask. FpCategory::Infinite diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 2bc897224970d..885f7608a337e 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -662,10 +662,7 @@ impl f32 { // hardware flushes subnormals to zero. These are platforms bugs, and Rust will misbehave on // such hardware, but we can at least try to make things seem as sane as possible by being // careful here. - // - // FIXME(jubilee): Using x87 operations is never necessary in order to function - // on x86 processors for Rust-to-Rust calls, so this issue should not happen. - // Code generation should be adjusted to use non-C calling conventions, avoiding this. + // see also https://github.com/rust-lang/rust/issues/114479 if self.is_infinite() { // A value may compare unequal to infinity, despite having a "full" exponent mask. FpCategory::Infinite diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index b3f5be9fc8a46..28cc231ccc76d 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -660,10 +660,7 @@ impl f64 { // float semantics Rust relies on: x87 uses a too-large exponent, and some hardware flushes // subnormals to zero. These are platforms bugs, and Rust will misbehave on such hardware, // but we can at least try to make things seem as sane as possible by being careful here. - // - // FIXME(jubilee): Using x87 operations is never necessary in order to function - // on x86 processors for Rust-to-Rust calls, so this issue should not happen. - // Code generation should be adjusted to use non-C calling conventions, avoiding this. + // see also https://github.com/rust-lang/rust/issues/114479 // // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask. // And it may not be NaN, as it can simply be an "overextended" finite value. diff --git a/tests/ui/numbers-arithmetic/issue-105626.rs b/tests/ui/numbers-arithmetic/issue-105626.rs index f942cf1283d0d..8d4a7c308e7fe 100644 --- a/tests/ui/numbers-arithmetic/issue-105626.rs +++ b/tests/ui/numbers-arithmetic/issue-105626.rs @@ -11,6 +11,7 @@ fn main() { assert_ne!((n as f64) as f32, n as f32); // FIXME: these assertions fail if only x87 is enabled + // see also https://github.com/rust-lang/rust/issues/114479 assert_eq!(n as i64 as f32, r); assert_eq!(n as u64 as f32, r); }