@@ -529,7 +529,7 @@ impl f32 {
529
529
#[ inline]
530
530
#[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
531
531
pub ( crate ) const fn abs_private ( self ) -> f32 {
532
- // SAFETY: This transmutation is fine. Probably. For the reasons std is using it .
532
+ // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits` .
533
533
unsafe { mem:: transmute :: < u32 , f32 > ( mem:: transmute :: < f32 , u32 > ( self ) & !Self :: SIGN_MASK ) }
534
534
}
535
535
@@ -654,18 +654,20 @@ 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. These are platforms bugs, and Rust will misbehave on
663
+ // such hardware, but we can at least try to make things seem as sane as possible by being
664
+ // careful here.
659
665
//
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
666
// FIXME(jubilee): Using x87 operations is never necessary in order to function
664
667
// on x86 processors for Rust-to-Rust calls, so this issue should not happen.
665
668
// Code generation should be adjusted to use non-C calling conventions, avoiding this.
666
- //
667
669
if self . is_infinite ( ) {
668
- // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
670
+ // A value may compare unequal to infinity, despite having a "full" exponent mask.
669
671
FpCategory :: Infinite
670
672
} else if self . is_nan ( ) {
671
673
// And it may not be NaN, as it can simply be an "overextended" finite value.
@@ -675,48 +677,20 @@ impl f32 {
675
677
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
676
678
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
677
679
// Most of std's targets don't use those, but they are used for thumbv7neon.
678
- // So, this does use bitpattern matching for the rest.
679
-
680
- // SAFETY: f32 to u32 is fine. Usually.
681
- // If classify has gotten this far, the value is definitely in one of these categories.
682
- unsafe { f32:: partial_classify ( self ) }
683
- }
684
- }
685
-
686
- // This doesn't actually return a right answer for NaN on purpose,
687
- // seeing as how it cannot correctly discern between a floating point NaN,
688
- // and some normal floating point numbers truncated from an x87 FPU.
689
- // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
690
- // like the f64 version does, but I need to run more checks on how things go on x86.
691
- // I fear losing mantissa data that would have answered that differently.
692
- //
693
- // # Safety
694
- // This requires making sure you call this function for values it answers correctly on,
695
- // otherwise it returns a wrong answer. This is not important for memory safety per se,
696
- // but getting floats correct is important for not accidentally leaking const eval
697
- // runtime-deviating logic which may or may not be acceptable.
698
- #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
699
- const unsafe fn partial_classify ( self ) -> FpCategory {
700
- // SAFETY: The caller is not asking questions for which this will tell lies.
701
- let b = unsafe { mem:: transmute :: < f32 , u32 > ( self ) } ;
702
- match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
703
- ( 0 , 0 ) => FpCategory :: Zero ,
704
- ( _, 0 ) => FpCategory :: Subnormal ,
705
- _ => FpCategory :: Normal ,
706
- }
707
- }
708
-
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 ,
680
+ // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
681
+ // float codegen on this hardware, this doesn't actually return a right answer for NaN
682
+ // because it cannot correctly discern between a floating point NaN, and some normal
683
+ // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
684
+ // we are fine.
685
+ // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
686
+ // like the f64 version does, but I need to run more checks on how things go on x86.
687
+ // I fear losing mantissa data that would have answered that differently.
688
+ let b = self . to_bits ( ) ;
689
+ match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
690
+ ( 0 , 0 ) => FpCategory :: Zero ,
691
+ ( _, 0 ) => FpCategory :: Subnormal ,
692
+ _ => FpCategory :: Normal ,
693
+ }
720
694
}
721
695
}
722
696
@@ -1143,51 +1117,7 @@ impl f32 {
1143
1117
#[ inline]
1144
1118
pub const fn to_bits ( self ) -> u32 {
1145
1119
// SAFETY: `u32` is a plain old datatype so we can always transmute to it.
1146
- // ...sorta.
1147
- //
1148
- // It turns out that at runtime, it is possible for a floating point number
1149
- // to be subject to a floating point mode that alters nonzero subnormal numbers
1150
- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1151
- // This is not a problem per se, but at least one tier2 platform for Rust
1152
- // actually exhibits this behavior by default.
1153
- //
1154
- // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
1155
- // i.e. not soft-float, the way Rust does parameter passing can actually alter
1156
- // a number that is "not infinity" to have the same exponent as infinity,
1157
- // in a slightly unpredictable manner.
1158
- //
1159
- // And, of course evaluating to a NaN value is fairly nondeterministic.
1160
- // More precisely: when NaN should be returned is knowable, but which NaN?
1161
- // So far that's defined by a combination of LLVM and the CPU, not Rust.
1162
- // This function, however, allows observing the bitstring of a NaN,
1163
- // thus introspection on CTFE.
1164
- //
1165
- // In order to preserve, at least for the moment, const-to-runtime equivalence,
1166
- // we reject any of these possible situations from happening.
1167
- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1168
- const fn ct_f32_to_u32 ( ct : f32 ) -> u32 {
1169
- match ct. classify ( ) {
1170
- FpCategory :: Nan => {
1171
- panic ! ( "const-eval error: cannot use f32::to_bits on a NaN" )
1172
- }
1173
- FpCategory :: Subnormal => {
1174
- panic ! ( "const-eval error: cannot use f32::to_bits on a subnormal number" )
1175
- }
1176
- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
1177
- // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
1178
- unsafe { mem:: transmute :: < f32 , u32 > ( ct) }
1179
- }
1180
- }
1181
- }
1182
-
1183
- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
1184
- fn rt_f32_to_u32 ( x : f32 ) -> u32 {
1185
- // SAFETY: `u32` is a plain old datatype so we can always... uh...
1186
- // ...look, just pretend you forgot what you just read.
1187
- // Stability concerns.
1188
- unsafe { mem:: transmute ( x) }
1189
- }
1190
- intrinsics:: const_eval_select ( ( self , ) , ct_f32_to_u32, rt_f32_to_u32)
1120
+ unsafe { mem:: transmute ( self ) }
1191
1121
}
1192
1122
1193
1123
/// Raw transmutation from `u32`.
@@ -1232,53 +1162,8 @@ impl f32 {
1232
1162
#[ inline]
1233
1163
pub const fn from_bits ( v : u32 ) -> Self {
1234
1164
// It turns out the safety issues with sNaN were overblown! Hooray!
1235
- // SAFETY: `u32` is a plain old datatype so we can always transmute from it
1236
- // ...sorta.
1237
- //
1238
- // It turns out that at runtime, it is possible for a floating point number
1239
- // to be subject to floating point modes that alter nonzero subnormal numbers
1240
- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1241
- // This is not a problem usually, but at least one tier2 platform for Rust
1242
- // actually exhibits this behavior by default: thumbv7neon
1243
- // aka "the Neon FPU in AArch32 state"
1244
- //
1245
- // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
1246
- // i.e. not soft-float, the way Rust does parameter passing can actually alter
1247
- // a number that is "not infinity" to have the same exponent as infinity,
1248
- // in a slightly unpredictable manner.
1249
- //
1250
- // And, of course evaluating to a NaN value is fairly nondeterministic.
1251
- // More precisely: when NaN should be returned is knowable, but which NaN?
1252
- // So far that's defined by a combination of LLVM and the CPU, not Rust.
1253
- // This function, however, allows observing the bitstring of a NaN,
1254
- // thus introspection on CTFE.
1255
- //
1256
- // In order to preserve, at least for the moment, const-to-runtime equivalence,
1257
- // reject any of these possible situations from happening.
1258
- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1259
- const fn ct_u32_to_f32 ( ct : u32 ) -> f32 {
1260
- match f32:: classify_bits ( ct) {
1261
- FpCategory :: Subnormal => {
1262
- panic ! ( "const-eval error: cannot use f32::from_bits on a subnormal number" )
1263
- }
1264
- FpCategory :: Nan => {
1265
- panic ! ( "const-eval error: cannot use f32::from_bits on NaN" )
1266
- }
1267
- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
1268
- // SAFETY: It's not a frumious number
1269
- unsafe { mem:: transmute :: < u32 , f32 > ( ct) }
1270
- }
1271
- }
1272
- }
1273
-
1274
- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
1275
- fn rt_u32_to_f32 ( x : u32 ) -> f32 {
1276
- // SAFETY: `u32` is a plain old datatype so we can always... uh...
1277
- // ...look, just pretend you forgot what you just read.
1278
- // Stability concerns.
1279
- unsafe { mem:: transmute ( x) }
1280
- }
1281
- intrinsics:: const_eval_select ( ( v, ) , ct_u32_to_f32, rt_u32_to_f32)
1165
+ // SAFETY: `u32` is a plain old datatype so we can always transmute from it.
1166
+ unsafe { mem:: transmute ( v) }
1282
1167
}
1283
1168
1284
1169
/// Returns the memory representation of this floating point number as a byte array in
0 commit comments