@@ -449,7 +449,8 @@ impl f32 {
449
449
#[ inline]
450
450
#[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
451
451
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 ) }
453
454
}
454
455
455
456
/// Returns `true` if this value is positive infinity or negative infinity, and
@@ -472,7 +473,10 @@ impl f32 {
472
473
#[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
473
474
#[ inline]
474
475
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 )
476
480
}
477
481
478
482
/// Returns `true` if this number is neither infinite nor `NaN`.
@@ -568,15 +572,53 @@ impl f32 {
568
572
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
569
573
#[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
570
574
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 {
571
614
const EXP_MASK : u32 = 0x7f800000 ;
572
615
const MAN_MASK : u32 = 0x007fffff ;
573
616
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 ) {
576
620
( 0 , 0 ) => FpCategory :: Zero ,
577
621
( _, 0 ) => FpCategory :: Subnormal ,
578
- ( 0 , EXP_MASK ) => FpCategory :: Infinite ,
579
- ( _, EXP_MASK ) => FpCategory :: Nan ,
580
622
_ => FpCategory :: Normal ,
581
623
}
582
624
}
@@ -616,7 +658,8 @@ impl f32 {
616
658
pub const fn is_sign_negative ( self ) -> bool {
617
659
// IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
618
660
// 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 }
620
663
}
621
664
622
665
/// Takes the reciprocal (inverse) of a number, `1/x`.
@@ -878,7 +921,7 @@ impl f32 {
878
921
pub const fn from_bits ( v : u32 ) -> Self {
879
922
// SAFETY: `u32` is a plain old datatype so we can always transmute from it
880
923
// It turns out the safety issues with sNaN were overblown! Hooray!
881
- unsafe { mem:: transmute ( v) }
924
+ unsafe { mem:: transmute :: < u32 , f32 > ( v) }
882
925
}
883
926
884
927
/// Return the memory representation of this floating point number as a byte array in
0 commit comments