12
12
#![ unstable( feature = "f16" , issue = "116909" ) ]
13
13
14
14
use crate :: convert:: FloatToInt ;
15
- #[ cfg( not( test) ) ]
16
- use crate :: intrinsics;
17
15
use crate :: mem;
18
16
use crate :: num:: FpCategory ;
19
17
@@ -426,15 +424,15 @@ impl f16 {
426
424
pub const fn classify ( self ) -> FpCategory {
427
425
// A previous implementation for f32/f64 tried to only use bitmask-based checks,
428
426
// using `to_bits` to transmute the float to its bit repr and match on that.
429
- // Unfortunately, floating point numbers can be much worse than that.
430
- // This also needs to not result in recursive evaluations of `to_bits` .
427
+ // If we only cared about being "technically" correct, that's an entirely legit
428
+ // implementation .
431
429
//
432
-
433
- // Platforms without native support generally convert to `f32` to perform operations,
434
- // and most of these platforms correctly round back to `f16` after each operation .
435
- // However, some platforms have bugs where they keep the excess `f32` precision (e.g.
436
- // WASM, see llvm/llvm-project#96437). This implementation makes a best-effort attempt
437
- // to account for that excess precision .
430
+ // Unfortunately, there are platforms out there that do not correctly implement the IEEE
431
+ // float semantics Rust relies on: some hardware flushes denormals to zero, and some
432
+ // platforms convert to `f32` to perform operations without properly rounding back (e.g .
433
+ // WASM, see llvm/llvm-project#96437). These are platforms bugs, and Rust will misbehave on
434
+ // such platforms, but we can at least try to make things seem as sane as possible by being
435
+ // careful here .
438
436
if self . is_infinite ( ) {
439
437
// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
440
438
FpCategory :: Infinite
@@ -447,45 +445,19 @@ impl f16 {
447
445
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
448
446
// Most of std's targets don't use those, but they are used for thumbv7neon.
449
447
// So, this does use bitpattern matching for the rest.
450
-
451
- // SAFETY: f16 to u16 is fine. Usually.
452
- // If classify has gotten this far, the value is definitely in one of these categories.
453
- unsafe { f16:: partial_classify ( self ) }
454
- }
455
- }
456
-
457
- /// This doesn't actually return a right answer for NaN on purpose,
458
- /// seeing as how it cannot correctly discern between a floating point NaN,
459
- /// and some normal floating point numbers truncated from an x87 FPU.
460
- ///
461
- /// # Safety
462
- ///
463
- /// This requires making sure you call this function for values it answers correctly on,
464
- /// otherwise it returns a wrong answer. This is not important for memory safety per se,
465
- /// but getting floats correct is important for not accidentally leaking const eval
466
- /// runtime-deviating logic which may or may not be acceptable.
467
- #[ inline]
468
- #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
469
- const unsafe fn partial_classify ( self ) -> FpCategory {
470
- // SAFETY: The caller is not asking questions for which this will tell lies.
471
- let b = unsafe { mem:: transmute :: < f16 , u16 > ( self ) } ;
472
- match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
473
- ( 0 , Self :: EXP_MASK ) => FpCategory :: Infinite ,
474
- ( 0 , 0 ) => FpCategory :: Zero ,
475
- ( _, 0 ) => FpCategory :: Subnormal ,
476
- _ => FpCategory :: Normal ,
448
+ f16:: partial_classify ( self )
477
449
}
478
450
}
479
451
480
- /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
481
- /// FIXME(jubilee): In a just world, this would be the entire impl for classify,
482
- /// plus a transmute. We do not live in a just world, but we can make it more so .
452
+ /// On x87, due do the incorrect float codegen on this hardware, this doesn't actually return a
453
+ /// right answer for NaN because it cannot correctly discern between a floating point NaN, and
454
+ /// some normal floating point numbers truncated from an x87 FPU .
483
455
#[ inline]
484
456
#[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
485
- const fn classify_bits ( b : u16 ) -> FpCategory {
457
+ const fn partial_classify ( self ) -> FpCategory {
458
+ let b = self . to_bits ( ) ;
486
459
match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
487
460
( 0 , Self :: EXP_MASK ) => FpCategory :: Infinite ,
488
- ( _, Self :: EXP_MASK ) => FpCategory :: Nan ,
489
461
( 0 , 0 ) => FpCategory :: Zero ,
490
462
( _, 0 ) => FpCategory :: Subnormal ,
491
463
_ => FpCategory :: Normal ,
@@ -781,48 +753,7 @@ impl f16 {
781
753
#[ must_use = "this returns the result of the operation, without modifying the original" ]
782
754
pub const fn to_bits ( self ) -> u16 {
783
755
// SAFETY: `u16` is a plain old datatype so we can always transmute to it.
784
- // ...sorta.
785
- //
786
- // It turns out that at runtime, it is possible for a floating point number
787
- // to be subject to a floating point mode that alters nonzero subnormal numbers
788
- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
789
- //
790
- // And, of course evaluating to a NaN value is fairly nondeterministic.
791
- // More precisely: when NaN should be returned is knowable, but which NaN?
792
- // So far that's defined by a combination of LLVM and the CPU, not Rust.
793
- // This function, however, allows observing the bitstring of a NaN,
794
- // thus introspection on CTFE.
795
- //
796
- // In order to preserve, at least for the moment, const-to-runtime equivalence,
797
- // we reject any of these possible situations from happening.
798
- #[ inline]
799
- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
800
- const fn ct_f16_to_u16 ( ct : f16 ) -> u16 {
801
- // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
802
- // want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
803
- // convention discrepancies calling intrinsics). So just classify the bits instead.
804
-
805
- // SAFETY: this is a POD transmutation
806
- let bits = unsafe { mem:: transmute :: < f16 , u16 > ( ct) } ;
807
- match f16:: classify_bits ( bits) {
808
- FpCategory :: Nan => {
809
- panic ! ( "const-eval error: cannot use f16::to_bits on a NaN" )
810
- }
811
- FpCategory :: Subnormal => {
812
- panic ! ( "const-eval error: cannot use f16::to_bits on a subnormal number" )
813
- }
814
- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => bits,
815
- }
816
- }
817
-
818
- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
819
- fn rt_f16_to_u16 ( x : f16 ) -> u16 {
820
- // SAFETY: `u16` is a plain old datatype so we can always... uh...
821
- // ...look, just pretend you forgot what you just read.
822
- // Stability concerns.
823
- unsafe { mem:: transmute ( x) }
824
- }
825
- intrinsics:: const_eval_select ( ( self , ) , ct_f16_to_u16, rt_f16_to_u16)
756
+ unsafe { mem:: transmute ( self ) }
826
757
}
827
758
828
759
/// Raw transmutation from `u16`.
@@ -869,49 +800,8 @@ impl f16 {
869
800
#[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
870
801
pub const fn from_bits ( v : u16 ) -> Self {
871
802
// It turns out the safety issues with sNaN were overblown! Hooray!
872
- // SAFETY: `u16` is a plain old datatype so we can always transmute from it
873
- // ...sorta.
874
- //
875
- // It turns out that at runtime, it is possible for a floating point number
876
- // to be subject to floating point modes that alter nonzero subnormal numbers
877
- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
878
- // This is not a problem usually, but at least one tier2 platform for Rust
879
- // actually exhibits this behavior by default: thumbv7neon
880
- // aka "the Neon FPU in AArch32 state"
881
- //
882
- // And, of course evaluating to a NaN value is fairly nondeterministic.
883
- // More precisely: when NaN should be returned is knowable, but which NaN?
884
- // So far that's defined by a combination of LLVM and the CPU, not Rust.
885
- // This function, however, allows observing the bitstring of a NaN,
886
- // thus introspection on CTFE.
887
- //
888
- // In order to preserve, at least for the moment, const-to-runtime equivalence,
889
- // reject any of these possible situations from happening.
890
- #[ inline]
891
- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
892
- const fn ct_u16_to_f16 ( ct : u16 ) -> f16 {
893
- match f16:: classify_bits ( ct) {
894
- FpCategory :: Subnormal => {
895
- panic ! ( "const-eval error: cannot use f16::from_bits on a subnormal number" )
896
- }
897
- FpCategory :: Nan => {
898
- panic ! ( "const-eval error: cannot use f16::from_bits on NaN" )
899
- }
900
- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
901
- // SAFETY: It's not a frumious number
902
- unsafe { mem:: transmute :: < u16 , f16 > ( ct) }
903
- }
904
- }
905
- }
906
-
907
- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
908
- fn rt_u16_to_f16 ( x : u16 ) -> f16 {
909
- // SAFETY: `u16` is a plain old datatype so we can always... uh...
910
- // ...look, just pretend you forgot what you just read.
911
- // Stability concerns.
912
- unsafe { mem:: transmute ( x) }
913
- }
914
- intrinsics:: const_eval_select ( ( v, ) , ct_u16_to_f16, rt_u16_to_f16)
803
+ // SAFETY: `u16` is a plain old datatype so we can always transmute from it.
804
+ unsafe { mem:: transmute ( v) }
915
805
}
916
806
917
807
/// Returns the memory representation of this floating point number as a byte array in
0 commit comments