@@ -654,18 +654,19 @@ impl f32 {
654
654
pub const fn classify ( self ) -> FpCategory {
655
655
// A previous implementation tried to only use bitmask-based checks,
656
656
// 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.
659
664
//
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.
663
665
// FIXME(jubilee): Using x87 operations is never necessary in order to function
664
666
// on x86 processors for Rust-to-Rust calls, so this issue should not happen.
665
667
// Code generation should be adjusted to use non-C calling conventions, avoiding this.
666
- //
667
668
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.
669
670
FpCategory :: Infinite
670
671
} else if self . is_nan ( ) {
671
672
// And it may not be NaN, as it can simply be an "overextended" finite value.
@@ -706,20 +707,6 @@ impl f32 {
706
707
}
707
708
}
708
709
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
-
723
710
/// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
724
711
/// positive sign bit and positive infinity.
725
712
///
@@ -1140,51 +1127,7 @@ impl f32 {
1140
1127
#[ inline]
1141
1128
pub const fn to_bits ( self ) -> u32 {
1142
1129
// 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 ) }
1188
1131
}
1189
1132
1190
1133
/// Raw transmutation from `u32`.
@@ -1229,53 +1172,8 @@ impl f32 {
1229
1172
#[ inline]
1230
1173
pub const fn from_bits ( v : u32 ) -> Self {
1231
1174
// 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) }
1279
1177
}
1280
1178
1281
1179
/// Returns the memory representation of this floating point number as a byte array in
0 commit comments