@@ -449,7 +449,8 @@ impl f32 {
449449 #[ inline]
450450 #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
451451 pub ( crate ) const fn abs_private ( self ) -> f32 {
452- f32:: from_bits ( self . to_bits ( ) & 0x7fff_ffff )
452+ // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
453+ unsafe { mem:: transmute :: < u32 , f32 > ( mem:: transmute :: < f32 , u32 > ( self ) & 0x7fff_ffff ) }
453454 }
454455
455456 /// Returns `true` if this value is positive infinity or negative infinity, and
@@ -472,7 +473,10 @@ impl f32 {
472473 #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
473474 #[ inline]
474475 pub const fn is_infinite ( self ) -> bool {
475- self . abs_private ( ) == Self :: INFINITY
476+ // Getting clever with transmutation can result in incorrect answers on some FPUs
477+ // FIXME: alter the Rust <-> Rust calling convention to prevent this problem.
478+ // See https://github.com/rust-lang/rust/issues/72327
479+ ( self == f32:: INFINITY ) | ( self == f32:: NEG_INFINITY )
476480 }
477481
478482 /// Returns `true` if this number is neither infinite nor `NaN`.
@@ -568,15 +572,53 @@ impl f32 {
568572 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
569573 #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
570574 pub const fn classify ( self ) -> FpCategory {
575+ // A previous implementation tried to only use bitmask-based checks,
576+ // using f32::to_bits to transmute the float to its bit repr and match on that.
577+ // Unfortunately, floating point numbers can be much worse than that.
578+ // This also needs to not result in recursive evaluations of f64::to_bits.
579+ //
580+ // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
581+ // in spite of a request for them using f32 and f64, to things like x87 operations.
582+ // These have an f64's mantissa, but can have a larger than normal exponent.
583+ // FIXME(jubilee): Using x87 operations is never necessary in order to function
584+ // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
585+ // Code generation should be adjusted to use non-C calling conventions, avoiding this.
586+ //
587+ if self . is_infinite ( ) {
588+ // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
589+ FpCategory :: Infinite
590+ } else if self . is_nan ( ) {
591+ // And it may not be NaN, as it can simply be an "overextended" finite value.
592+ FpCategory :: Nan
593+ } else {
594+ // However, std can't simply compare to zero to check for zero, either,
595+ // as correctness requires avoiding equality tests that may be Subnormal == -0.0
596+ // because it may be wrong under "denormals are zero" and "flush to zero" modes.
597+ // Most of std's targets don't use those, but they are used for thumbv7neon".
598+ // So, this does use bitpattern matching for the rest.
599+
600+ // SAFETY: f32 to u32 is fine. Usually.
601+ // If classify has gotten this far, the value is definitely in one of these categories.
602+ unsafe { f32:: partial_classify ( self ) }
603+ }
604+ }
605+
606+ // This doesn't actually return a right answer for NaN on purpose,
607+ // seeing as how it cannot correctly discern between a floating point NaN,
608+ // and some normal floating point numbers truncated from an x87 FPU.
609+ // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
610+ // like the f64 version does, but I need to run more checks on how things go on x86.
611+ // I fear losing mantissa data that would have answered that differently.
612+ #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
613+ const unsafe fn partial_classify ( self ) -> FpCategory {
571614 const EXP_MASK : u32 = 0x7f800000 ;
572615 const MAN_MASK : u32 = 0x007fffff ;
573616
574- let bits = self . to_bits ( ) ;
575- match ( bits & MAN_MASK , bits & EXP_MASK ) {
617+ // SAFETY: The caller is not asking questions for which this will tell lies.
618+ let b = unsafe { mem:: transmute :: < f32 , u32 > ( self ) } ;
619+ match ( b & MAN_MASK , b & EXP_MASK ) {
576620 ( 0 , 0 ) => FpCategory :: Zero ,
577621 ( _, 0 ) => FpCategory :: Subnormal ,
578- ( 0 , EXP_MASK ) => FpCategory :: Infinite ,
579- ( _, EXP_MASK ) => FpCategory :: Nan ,
580622 _ => FpCategory :: Normal ,
581623 }
582624 }
@@ -616,7 +658,8 @@ impl f32 {
616658 pub const fn is_sign_negative ( self ) -> bool {
617659 // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
618660 // applies to zeros and NaNs as well.
619- self . to_bits ( ) & 0x8000_0000 != 0
661+ // SAFETY: This is just transmuting to get the sign bit, it's fine.
662+ unsafe { mem:: transmute :: < f32 , u32 > ( self ) & 0x8000_0000 != 0 }
620663 }
621664
622665 /// Takes the reciprocal (inverse) of a number, `1/x`.
@@ -878,7 +921,7 @@ impl f32 {
878921 pub const fn from_bits ( v : u32 ) -> Self {
879922 // SAFETY: `u32` is a plain old datatype so we can always transmute from it
880923 // It turns out the safety issues with sNaN were overblown! Hooray!
881- unsafe { mem:: transmute ( v) }
924+ unsafe { mem:: transmute :: < u32 , f32 > ( v) }
882925 }
883926
884927 /// Return the memory representation of this floating point number as a byte array in
0 commit comments