about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-17 09:13:13 +0000
committerbors <bors@rust-lang.org>2024-08-17 09:13:13 +0000
commit426a60abc213b28a7c7198e475476b6e650d871f (patch)
tree6f939ee30d707932a72c16735b72227a054ef937
parent54a50bd86f5927a9dc56482e47b9068b176c828f (diff)
parent5f33085a7f574b95609da59b2d4164238d7782f0 (diff)
downloadrust-426a60abc213b28a7c7198e475476b6e650d871f.tar.gz
rust-426a60abc213b28a7c7198e475476b6e650d871f.zip
Auto merge of #128598 - RalfJung:float-comments, r=workingjubilee
float to/from bits and classify: update for float semantics RFC

With https://github.com/rust-lang/rfcs/pull/3514 having been accepted, it is clear that hardware which e.g. flushes subnormal to zero is just non-conformant from a Rust perspective -- this is a hardware bug, or maybe an LLVM backend bug (where LLVM doesn't lower floating-point ops in a way that they have the standardized behavior). So update the comments here to make it clear that we don't have to do any of this, we're just being nice.

Also remove the subnormal/NaN checks from the (unstable) const-version of to/from-bits; they are not needed since we decided with the aforementioned RFC that it is okay to get a different result at const-time and at run-time.

r? `@workingjubilee` since I think you wrote many of the comments I am editing here.
-rw-r--r--library/core/src/num/f128.rs110
-rw-r--r--library/core/src/num/f16.rs163
-rw-r--r--library/core/src/num/f32.rs169
-rw-r--r--library/core/src/num/f64.rs143
-rw-r--r--tests/ui/consts/const-float-bits-conv.rs31
-rw-r--r--tests/ui/consts/const-float-bits-reject-conv.rs68
-rw-r--r--tests/ui/consts/const-float-bits-reject-conv.stderr115
7 files changed, 116 insertions, 683 deletions
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index 0c04f47fe7d..38e69e7641a 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -290,7 +290,7 @@ impl f128 {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f128 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe {
             mem::transmute::<u128, f128>(mem::transmute::<f128, u128>(self) & !Self::SIGN_MASK)
         }
@@ -439,22 +439,12 @@ impl f128 {
     #[unstable(feature = "f128", issue = "116909")]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub const fn classify(self) -> FpCategory {
-        // Other float types cannot use a bitwise classify because they may suffer a variety
-        // of errors if the backend chooses to cast to different float types (x87). `f128` cannot
-        // fit into any other float types so this is not a concern, and we rely on bit patterns.
+        // Other float types suffer from various platform bugs that violate the usual IEEE semantics
+        // and also make bitwise classification not always work reliably. However, `f128` cannot fit
+        // into any other float types so this is not a concern, and we can rely on bit patterns.
 
-        // SAFETY: POD bitcast, same as in `to_bits`.
-        let bits = unsafe { mem::transmute::<f128, u128>(self) };
-        Self::classify_bits(bits)
-    }
-
-    /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    /// FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    /// plus a transmute. We do not live in a just world, but we can make it more so.
-    #[inline]
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u128) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+        let bits = self.to_bits();
+        match (bits & Self::MAN_MASK, bits & Self::EXP_MASK) {
             (0, Self::EXP_MASK) => FpCategory::Infinite,
             (_, Self::EXP_MASK) => FpCategory::Nan,
             (0, 0) => FpCategory::Zero,
@@ -922,48 +912,7 @@ impl f128 {
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn to_bits(self) -> u128 {
         // SAFETY: `u128` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to a floating point mode that alters nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // we reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f128_to_u128(ct: f128) -> u128 {
-            // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
-            // is not available on all platforms (needs `netf2` and `unordtf2`). So classify
-            // the bits instead.
-
-            // SAFETY: this is a POD transmutation
-            let bits = unsafe { mem::transmute::<f128, u128>(ct) };
-            match f128::classify_bits(bits) {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f128::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f128::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f128_to_u128(x: f128) -> u128 {
-            // SAFETY: `u128` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((self,), ct_f128_to_u128, rt_f128_to_u128)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u128`.
@@ -1011,49 +960,8 @@ impl f128 {
     #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
     pub const fn from_bits(v: u128) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u128` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u128_to_f128(ct: u128) -> f128 {
-            match f128::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f128::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f128::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u128, f128>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u128_to_f128(x: u128) -> f128 {
-            // SAFETY: `u128` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((v,), ct_u128_to_f128, rt_u128_to_f128)
+        // SAFETY: `u128` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index e5b1148e192..41bd34a2702 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -284,7 +284,7 @@ impl f16 {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f16 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe { mem::transmute::<u16, f16>(mem::transmute::<f16, u16>(self) & !Self::SIGN_MASK) }
     }
 
@@ -426,15 +426,15 @@ impl f16 {
     pub const fn classify(self) -> FpCategory {
         // A previous implementation for f32/f64 tried to only use bitmask-based checks,
         // using `to_bits` to transmute the float to its bit repr and match on that.
-        // Unfortunately, floating point numbers can be much worse than that.
-        // This also needs to not result in recursive evaluations of `to_bits`.
+        // If we only cared about being "technically" correct, that's an entirely legit
+        // implementation.
         //
-
-        // Platforms without native support generally convert to `f32` to perform operations,
-        // and most of these platforms correctly round back to `f16` after each operation.
-        // However, some platforms have bugs where they keep the excess `f32` precision (e.g.
-        // WASM, see llvm/llvm-project#96437). This implementation makes a best-effort attempt
-        // to account for that excess precision.
+        // Unfortunately, there are platforms out there that do not correctly implement the IEEE
+        // float semantics Rust relies on: some hardware flushes denormals to zero, and some
+        // platforms convert to `f32` to perform operations without properly rounding back (e.g.
+        // 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.
         if self.is_infinite() {
             // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
             FpCategory::Infinite
@@ -446,49 +446,20 @@ impl f16 {
             // as correctness requires avoiding equality tests that may be Subnormal == -0.0
             // because it may be wrong under "denormals are zero" and "flush to zero" modes.
             // Most of std's targets don't use those, but they are used for thumbv7neon.
-            // So, this does use bitpattern matching for the rest.
-
-            // SAFETY: f16 to u16 is fine. Usually.
-            // If classify has gotten this far, the value is definitely in one of these categories.
-            unsafe { f16::partial_classify(self) }
-        }
-    }
-
-    /// This doesn't actually return a right answer for NaN on purpose,
-    /// seeing as how it cannot correctly discern between a floating point NaN,
-    /// and some normal floating point numbers truncated from an x87 FPU.
-    ///
-    /// # Safety
-    ///
-    /// This requires making sure you call this function for values it answers correctly on,
-    /// otherwise it returns a wrong answer. This is not important for memory safety per se,
-    /// but getting floats correct is important for not accidentally leaking const eval
-    /// runtime-deviating logic which may or may not be acceptable.
-    #[inline]
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const unsafe fn partial_classify(self) -> FpCategory {
-        // SAFETY: The caller is not asking questions for which this will tell lies.
-        let b = unsafe { mem::transmute::<f16, u16>(self) };
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
-        }
-    }
-
-    /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    /// FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    /// plus a transmute. We do not live in a just world, but we can make it more so.
-    #[inline]
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u16) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (_, Self::EXP_MASK) => FpCategory::Nan,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
+            // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
+            // float codegen on this hardware, this doesn't actually return a right answer for NaN
+            // because it cannot correctly discern between a floating point NaN, and some normal
+            // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
+            // we are fine.
+            // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
+            // like the f64 version does, but I need to run more checks on how things go on x86.
+            // I fear losing mantissa data that would have answered that differently.
+            let b = self.to_bits();
+            match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+                (0, 0) => FpCategory::Zero,
+                (_, 0) => FpCategory::Subnormal,
+                _ => FpCategory::Normal,
+            }
         }
     }
 
@@ -952,48 +923,7 @@ impl f16 {
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn to_bits(self) -> u16 {
         // SAFETY: `u16` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to a floating point mode that alters nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // we reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f16_to_u16(ct: f16) -> u16 {
-            // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
-            // want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
-            // convention discrepancies calling intrinsics). So just classify the bits instead.
-
-            // SAFETY: this is a POD transmutation
-            let bits = unsafe { mem::transmute::<f16, u16>(ct) };
-            match f16::classify_bits(bits) {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f16::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f16::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f16_to_u16(x: f16) -> u16 {
-            // SAFETY: `u16` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((self,), ct_f16_to_u16, rt_f16_to_u16)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u16`.
@@ -1040,49 +970,8 @@ impl f16 {
     #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
     pub const fn from_bits(v: u16) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u16` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u16_to_f16(ct: u16) -> f16 {
-            match f16::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f16::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f16::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u16, f16>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u16_to_f16(x: u16) -> f16 {
-            // SAFETY: `u16` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((v,), ct_u16_to_f16, rt_u16_to_f16)
+        // SAFETY: `u16` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 7710e23edf0..719727e2f1e 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -529,7 +529,7 @@ impl f32 {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f32 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & !Self::SIGN_MASK) }
     }
 
@@ -654,18 +654,20 @@ impl f32 {
     pub const fn classify(self) -> FpCategory {
         // A previous implementation tried to only use bitmask-based checks,
         // using f32::to_bits to transmute the float to its bit repr and match on that.
-        // Unfortunately, floating point numbers can be much worse than that.
-        // This also needs to not result in recursive evaluations of f64::to_bits.
+        // If we only cared about being "technically" correct, that's an entirely legit
+        // implementation.
+        //
+        // Unfortunately, there is hardware out there that does not correctly implement the IEEE
+        // float semantics Rust relies on: x87 uses a too-large mantissa and 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.
         //
-        // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
-        // in spite of a request for them using f32 and f64, to things like x87 operations.
-        // These have an f64's mantissa, but can have a larger than normal exponent.
         // 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.
-        //
         if self.is_infinite() {
-            // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
+            // A value may compare unequal to infinity, despite having a "full" exponent mask.
             FpCategory::Infinite
         } else if self.is_nan() {
             // And it may not be NaN, as it can simply be an "overextended" finite value.
@@ -675,48 +677,20 @@ impl f32 {
             // as correctness requires avoiding equality tests that may be Subnormal == -0.0
             // because it may be wrong under "denormals are zero" and "flush to zero" modes.
             // Most of std's targets don't use those, but they are used for thumbv7neon.
-            // So, this does use bitpattern matching for the rest.
-
-            // SAFETY: f32 to u32 is fine. Usually.
-            // If classify has gotten this far, the value is definitely in one of these categories.
-            unsafe { f32::partial_classify(self) }
-        }
-    }
-
-    // This doesn't actually return a right answer for NaN on purpose,
-    // seeing as how it cannot correctly discern between a floating point NaN,
-    // and some normal floating point numbers truncated from an x87 FPU.
-    // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
-    // like the f64 version does, but I need to run more checks on how things go on x86.
-    // I fear losing mantissa data that would have answered that differently.
-    //
-    // # Safety
-    // This requires making sure you call this function for values it answers correctly on,
-    // otherwise it returns a wrong answer. This is not important for memory safety per se,
-    // but getting floats correct is important for not accidentally leaking const eval
-    // runtime-deviating logic which may or may not be acceptable.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const unsafe fn partial_classify(self) -> FpCategory {
-        // SAFETY: The caller is not asking questions for which this will tell lies.
-        let b = unsafe { mem::transmute::<f32, u32>(self) };
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
-        }
-    }
-
-    // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    // FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    // plus a transmute. We do not live in a just world, but we can make it more so.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u32) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (_, Self::EXP_MASK) => FpCategory::Nan,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
+            // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
+            // float codegen on this hardware, this doesn't actually return a right answer for NaN
+            // because it cannot correctly discern between a floating point NaN, and some normal
+            // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
+            // we are fine.
+            // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
+            // like the f64 version does, but I need to run more checks on how things go on x86.
+            // I fear losing mantissa data that would have answered that differently.
+            let b = self.to_bits();
+            match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+                (0, 0) => FpCategory::Zero,
+                (_, 0) => FpCategory::Subnormal,
+                _ => FpCategory::Normal,
+            }
         }
     }
 
@@ -1143,51 +1117,7 @@ impl f32 {
     #[inline]
     pub const fn to_bits(self) -> u32 {
         // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to a floating point mode that alters nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem per se, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default.
-        //
-        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
-        // i.e. not soft-float, the way Rust does parameter passing can actually alter
-        // a number that is "not infinity" to have the same exponent as infinity,
-        // in a slightly unpredictable manner.
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // we reject any of these possible situations from happening.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f32_to_u32(ct: f32) -> u32 {
-            match ct.classify() {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f32::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f32::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
-                    unsafe { mem::transmute::<f32, u32>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f32_to_u32(x: f32) -> u32 {
-            // SAFETY: `u32` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u32`.
@@ -1232,53 +1162,8 @@ impl f32 {
     #[inline]
     pub const fn from_bits(v: u32) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u32` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
-        // i.e. not soft-float, the way Rust does parameter passing can actually alter
-        // a number that is "not infinity" to have the same exponent as infinity,
-        // in a slightly unpredictable manner.
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u32_to_f32(ct: u32) -> f32 {
-            match f32::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f32::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f32::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u32, f32>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u32_to_f32(x: u32) -> f32 {
-            // SAFETY: `u32` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32)
+        // SAFETY: `u32` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index a89859be7ef..85eb152ad1f 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -528,7 +528,7 @@ impl f64 {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f64 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & !Self::SIGN_MASK) }
     }
 
@@ -653,12 +653,14 @@ impl f64 {
     pub const fn classify(self) -> FpCategory {
         // A previous implementation tried to only use bitmask-based checks,
         // using f64::to_bits to transmute the float to its bit repr and match on that.
-        // Unfortunately, floating point numbers can be much worse than that.
-        // This also needs to not result in recursive evaluations of f64::to_bits.
+        // If we only cared about being "technically" correct, that's an entirely legit
+        // implementation.
+        //
+        // Unfortunately, there is hardware out there that does not correctly implement the IEEE
+        // 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.
         //
-        // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
-        // in spite of a request for them using f32 and f64, to things like x87 operations.
-        // These have an f64's mantissa, but can have a larger than normal exponent.
         // 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.
@@ -672,41 +674,18 @@ impl f64 {
             // as correctness requires avoiding equality tests that may be Subnormal == -0.0
             // because it may be wrong under "denormals are zero" and "flush to zero" modes.
             // Most of std's targets don't use those, but they are used for thumbv7neon.
-            // So, this does use bitpattern matching for the rest.
-
-            // SAFETY: f64 to u64 is fine. Usually.
-            // If control flow has gotten this far, the value is definitely in one of the categories
-            // that f64::partial_classify can correctly analyze.
-            unsafe { f64::partial_classify(self) }
-        }
-    }
-
-    // This doesn't actually return a right answer for NaN on purpose,
-    // seeing as how it cannot correctly discern between a floating point NaN,
-    // and some normal floating point numbers truncated from an x87 FPU.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const unsafe fn partial_classify(self) -> FpCategory {
-        // SAFETY: The caller is not asking questions for which this will tell lies.
-        let b = unsafe { mem::transmute::<f64, u64>(self) };
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
-        }
-    }
-
-    // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    // FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    // plus a transmute. We do not live in a just world, but we can make it more so.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u64) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (_, Self::EXP_MASK) => FpCategory::Nan,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
+            // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
+            // float codegen on this hardware, this doesn't actually return a right answer for NaN
+            // because it cannot correctly discern between a floating point NaN, and some normal
+            // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
+            // we are fine.
+            let b = self.to_bits();
+            match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+                (0, Self::EXP_MASK) => FpCategory::Infinite,
+                (0, 0) => FpCategory::Zero,
+                (_, 0) => FpCategory::Subnormal,
+                _ => FpCategory::Normal,
+            }
         }
     }
 
@@ -1134,33 +1113,7 @@ impl f64 {
     #[inline]
     pub const fn to_bits(self) -> u64 {
         // SAFETY: `u64` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // See the SAFETY comment in f64::from_bits for more.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f64_to_u64(ct: f64) -> u64 {
-            match ct.classify() {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f64::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f64::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
-                    unsafe { mem::transmute::<f64, u64>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f64_to_u64(rt: f64) -> u64 {
-            // SAFETY: `u64` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute::<f64, u64>(rt) }
-        }
-        intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u64`.
@@ -1205,58 +1158,8 @@ impl f64 {
     #[inline]
     pub const fn from_bits(v: u64) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u64` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits an FTZ behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // Even with this, not all instructions exhibit the FTZ behaviors on thumbv7neon,
-        // so this should load the same bits if LLVM emits the "correct" instructions,
-        // but LLVM sometimes makes interesting choices about float optimization,
-        // and other FPUs may do similar. Thus, it is wise to indulge luxuriously in caution.
-        //
-        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
-        // i.e. not soft-float, the way Rust does parameter passing can actually alter
-        // a number that is "not infinity" to have the same exponent as infinity,
-        // in a slightly unpredictable manner.
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u64_to_f64(ct: u64) -> f64 {
-            match f64::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f64::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f64::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u64, f64>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u64_to_f64(rt: u64) -> f64 {
-            // SAFETY: `u64` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute::<u64, f64>(rt) }
-        }
-        intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64)
+        // SAFETY: `u64` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/tests/ui/consts/const-float-bits-conv.rs b/tests/ui/consts/const-float-bits-conv.rs
index ba8db4c23dc..45e8ea570ed 100644
--- a/tests/ui/consts/const-float-bits-conv.rs
+++ b/tests/ui/consts/const-float-bits-conv.rs
@@ -23,6 +23,11 @@ macro_rules! const_assert {
     };
 }
 
+fn has_broken_floats() -> bool {
+    // i586 targets are broken due to <https://github.com/rust-lang/rust/issues/114479>.
+    std::env::var("TARGET").is_ok_and(|v| v.contains("i586"))
+}
+
 fn f32() {
     const_assert!((1f32).to_bits(), 0x3f800000);
     const_assert!(u32::from_be_bytes(1f32.to_be_bytes()), 0x3f800000);
@@ -38,6 +43,19 @@ fn f32() {
     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);
+
+    // Check that NaNs roundtrip their bits regardless of signalingness
+    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+    // NOTE: These names assume `f{BITS}::NAN` is a quiet NAN and IEEE754-2008's NaN rules apply!
+    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);
+    if !has_broken_floats() {
+        const_assert!(f32::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN);
+    }
 }
 
 fn f64() {
@@ -55,6 +73,19 @@ fn f64() {
     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);
+
+    // Check that NaNs roundtrip their bits regardless of signalingness
+    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+    // NOTE: These names assume `f{BITS}::NAN` is a quiet NAN and IEEE754-2008's NaN rules apply!
+    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);
+    if !has_broken_floats() {
+        const_assert!(f64::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN);
+    }
 }
 
 fn main() {
diff --git a/tests/ui/consts/const-float-bits-reject-conv.rs b/tests/ui/consts/const-float-bits-reject-conv.rs
deleted file mode 100644
index febb272869a..00000000000
--- a/tests/ui/consts/const-float-bits-reject-conv.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-//@ compile-flags: -Zmir-opt-level=0
-//@ error-pattern: cannot use f32::to_bits on a NaN
-#![feature(const_float_bits_conv)]
-#![feature(const_float_classify)]
-
-// Don't promote
-const fn nop<T>(x: T) -> T { x }
-
-macro_rules! const_assert {
-    ($a:expr) => {
-        {
-            const _: () = assert!($a);
-            assert!(nop($a));
-        }
-    };
-    ($a:expr, $b:expr) => {
-        {
-            const _: () = assert!($a == $b);
-            assert_eq!(nop($a), nop($b));
-        }
-    };
-}
-
-fn f32() {
-    // Check that NaNs roundtrip their bits regardless of signalingness
-    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    // ...actually, let's just check that these break. :D
-    const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
-    //~^ inside
-    const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
-    //~^ inside
-
-    // The rest of the code is dead because the constants already fail to evaluate.
-
-    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-
-    // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern.
-    // In practice, this seems to only cause a problem on x86, since the most widely used calling
-    // convention mandates that floating point values are returned on the x87 FPU stack. See #73328.
-    // However, during CTFE we still preserve bit patterns (though that is not a guarantee).
-    const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-    const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-}
-
-fn f64() {
-    // Check that NaNs roundtrip their bits regardless of signalingness
-    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    // ...actually, let's just check that these break. :D
-    const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
-    //~^ inside
-    const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
-    //~^ inside
-
-    // The rest of the code is dead because the constants already fail to evaluate.
-
-    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-
-    // See comment above.
-    const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-    const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-}
-
-fn main() {
-    f32();
-    f64();
-}
diff --git a/tests/ui/consts/const-float-bits-reject-conv.stderr b/tests/ui/consts/const-float-bits-reject-conv.stderr
deleted file mode 100644
index 1511dab12b0..00000000000
--- a/tests/ui/consts/const-float-bits-reject-conv.stderr
+++ /dev/null
@@ -1,115 +0,0 @@
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-note: inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `core::f32::<impl f32>::to_bits`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `f32::MASKED_NAN1`
-  --> $DIR/const-float-bits-reject-conv.rs:28:30
-   |
-LL |     const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-note: inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `core::f32::<impl f32>::to_bits`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `f32::MASKED_NAN2`
-  --> $DIR/const-float-bits-reject-conv.rs:30:30
-   |
-LL |     const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:35:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:36:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:42:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:43:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-   |                                  ^^^^^^^^^^^
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-note: inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `core::f64::<impl f64>::to_bits`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `f64::MASKED_NAN1`
-  --> $DIR/const-float-bits-reject-conv.rs:50:30
-   |
-LL |     const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-note: inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `core::f64::<impl f64>::to_bits`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `f64::MASKED_NAN2`
-  --> $DIR/const-float-bits-reject-conv.rs:52:30
-   |
-LL |     const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:57:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:58:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:61:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:62:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-   |                                  ^^^^^^^^^^^
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0080`.