@@ -654,18 +654,19 @@ impl f32 {
654654 pub const fn classify ( self ) -> FpCategory {
655655 // A previous implementation tried to only use bitmask-based checks,
656656 // using f32::to_bits to transmute the float to its bit repr and match on that.
657- // Unfortunately, floating point numbers can be much worse than that.
658- // This also needs to not result in recursive evaluations of f64::to_bits.
657+ // If we only cared about being "technically" correct, that's an entirely legit
658+ // implementation.
659+ //
660+ // Unfortunately, there is hardware out there that does not correctly implement the IEEE
661+ // float semantics Rust relies on: x87 uses a too large mantissa and exponent, and some
662+ // hardware flushes subnormals to zero. Rust will misbehave on such hardware, but we can at
663+ // least try to make things seem as sane as possible by being careful here.
659664 //
660- // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
661- // in spite of a request for them using f32 and f64, to things like x87 operations.
662- // These have an f64's mantissa, but can have a larger than normal exponent.
663665 // FIXME(jubilee): Using x87 operations is never necessary in order to function
664666 // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
665667 // Code generation should be adjusted to use non-C calling conventions, avoiding this.
666- //
667668 if self . is_infinite ( ) {
668- // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
669+ // A value may compare unequal to infinity, despite having a "full" exponent mask.
669670 FpCategory :: Infinite
670671 } else if self . is_nan ( ) {
671672 // And it may not be NaN, as it can simply be an "overextended" finite value.
@@ -706,20 +707,6 @@ impl f32 {
706707 }
707708 }
708709
709- // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
710- // FIXME(jubilee): In a just world, this would be the entire impl for classify,
711- // plus a transmute. We do not live in a just world, but we can make it more so.
712- #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
713- const fn classify_bits ( b : u32 ) -> FpCategory {
714- match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
715- ( 0 , Self :: EXP_MASK ) => FpCategory :: Infinite ,
716- ( _, Self :: EXP_MASK ) => FpCategory :: Nan ,
717- ( 0 , 0 ) => FpCategory :: Zero ,
718- ( _, 0 ) => FpCategory :: Subnormal ,
719- _ => FpCategory :: Normal ,
720- }
721- }
722-
723710 /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
724711 /// positive sign bit and positive infinity.
725712 ///
@@ -1140,51 +1127,7 @@ impl f32 {
11401127 #[ inline]
11411128 pub const fn to_bits ( self ) -> u32 {
11421129 // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
1143- // ...sorta.
1144- //
1145- // It turns out that at runtime, it is possible for a floating point number
1146- // to be subject to a floating point mode that alters nonzero subnormal numbers
1147- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1148- // This is not a problem per se, but at least one tier2 platform for Rust
1149- // actually exhibits this behavior by default.
1150- //
1151- // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
1152- // i.e. not soft-float, the way Rust does parameter passing can actually alter
1153- // a number that is "not infinity" to have the same exponent as infinity,
1154- // in a slightly unpredictable manner.
1155- //
1156- // And, of course evaluating to a NaN value is fairly nondeterministic.
1157- // More precisely: when NaN should be returned is knowable, but which NaN?
1158- // So far that's defined by a combination of LLVM and the CPU, not Rust.
1159- // This function, however, allows observing the bitstring of a NaN,
1160- // thus introspection on CTFE.
1161- //
1162- // In order to preserve, at least for the moment, const-to-runtime equivalence,
1163- // we reject any of these possible situations from happening.
1164- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1165- const fn ct_f32_to_u32 ( ct : f32 ) -> u32 {
1166- match ct. classify ( ) {
1167- FpCategory :: Nan => {
1168- panic ! ( "const-eval error: cannot use f32::to_bits on a NaN" )
1169- }
1170- FpCategory :: Subnormal => {
1171- panic ! ( "const-eval error: cannot use f32::to_bits on a subnormal number" )
1172- }
1173- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
1174- // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
1175- unsafe { mem:: transmute :: < f32 , u32 > ( ct) }
1176- }
1177- }
1178- }
1179-
1180- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
1181- fn rt_f32_to_u32 ( x : f32 ) -> u32 {
1182- // SAFETY: `u32` is a plain old datatype so we can always... uh...
1183- // ...look, just pretend you forgot what you just read.
1184- // Stability concerns.
1185- unsafe { mem:: transmute ( x) }
1186- }
1187- intrinsics:: const_eval_select ( ( self , ) , ct_f32_to_u32, rt_f32_to_u32)
1130+ unsafe { mem:: transmute ( self ) }
11881131 }
11891132
11901133 /// Raw transmutation from `u32`.
@@ -1229,53 +1172,8 @@ impl f32 {
12291172 #[ inline]
12301173 pub const fn from_bits ( v : u32 ) -> Self {
12311174 // It turns out the safety issues with sNaN were overblown! Hooray!
1232- // SAFETY: `u32` is a plain old datatype so we can always transmute from it
1233- // ...sorta.
1234- //
1235- // It turns out that at runtime, it is possible for a floating point number
1236- // to be subject to floating point modes that alter nonzero subnormal numbers
1237- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1238- // This is not a problem usually, but at least one tier2 platform for Rust
1239- // actually exhibits this behavior by default: thumbv7neon
1240- // aka "the Neon FPU in AArch32 state"
1241- //
1242- // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
1243- // i.e. not soft-float, the way Rust does parameter passing can actually alter
1244- // a number that is "not infinity" to have the same exponent as infinity,
1245- // in a slightly unpredictable manner.
1246- //
1247- // And, of course evaluating to a NaN value is fairly nondeterministic.
1248- // More precisely: when NaN should be returned is knowable, but which NaN?
1249- // So far that's defined by a combination of LLVM and the CPU, not Rust.
1250- // This function, however, allows observing the bitstring of a NaN,
1251- // thus introspection on CTFE.
1252- //
1253- // In order to preserve, at least for the moment, const-to-runtime equivalence,
1254- // reject any of these possible situations from happening.
1255- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1256- const fn ct_u32_to_f32 ( ct : u32 ) -> f32 {
1257- match f32:: classify_bits ( ct) {
1258- FpCategory :: Subnormal => {
1259- panic ! ( "const-eval error: cannot use f32::from_bits on a subnormal number" )
1260- }
1261- FpCategory :: Nan => {
1262- panic ! ( "const-eval error: cannot use f32::from_bits on NaN" )
1263- }
1264- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
1265- // SAFETY: It's not a frumious number
1266- unsafe { mem:: transmute :: < u32 , f32 > ( ct) }
1267- }
1268- }
1269- }
1270-
1271- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
1272- fn rt_u32_to_f32 ( x : u32 ) -> f32 {
1273- // SAFETY: `u32` is a plain old datatype so we can always... uh...
1274- // ...look, just pretend you forgot what you just read.
1275- // Stability concerns.
1276- unsafe { mem:: transmute ( x) }
1277- }
1278- intrinsics:: const_eval_select ( ( v, ) , ct_u32_to_f32, rt_u32_to_f32)
1175+ // SAFETY: `u32` is a plain old datatype so we can always transmute from it.
1176+ unsafe { mem:: transmute ( v) }
12791177 }
12801178
12811179 /// Returns the memory representation of this floating point number as a byte array in
0 commit comments