+ // 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");
+ }
+ // SAFETY: It's not a frumious number
+ _ => unsafe { mem::transmute::<u64, f64>(ct) },
+ }
+ }
+ // SAFETY: `u64` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ let rt_u64_to_f64 = |rt| unsafe { mem::transmute::<u64, f64>(rt) };
+ // SAFETY: We use internal implementations that either always work or fail at compile time.
+ unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) }