@@ -284,7 +284,7 @@ impl f16 {
284
284
#[ inline]
285
285
#[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
286
286
pub ( crate ) const fn abs_private ( self ) -> f16 {
287
- // SAFETY: This transmutation is fine. Probably. For the reasons std is using it .
287
+ // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits` .
288
288
unsafe { mem:: transmute :: < u16 , f16 > ( mem:: transmute :: < f16 , u16 > ( self ) & !Self :: SIGN_MASK ) }
289
289
}
290
290
@@ -426,15 +426,15 @@ impl f16 {
426
426
pub const fn classify ( self ) -> FpCategory {
427
427
// A previous implementation for f32/f64 tried to only use bitmask-based checks,
428
428
// 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` .
429
+ // If we only cared about being "technically" correct, that's an entirely legit
430
+ // implementation .
431
431
//
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 .
432
+ // Unfortunately, there are platforms out there that do not correctly implement the IEEE
433
+ // float semantics Rust relies on: some hardware flushes denormals to zero, and some
434
+ // platforms convert to `f32` to perform operations without properly rounding back (e.g .
435
+ // WASM, see llvm/llvm-project#96437). These are platforms bugs, and Rust will misbehave on
436
+ // such platforms, but we can at least try to make things seem as sane as possible by being
437
+ // careful here .
438
438
if self . is_infinite ( ) {
439
439
// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
440
440
FpCategory :: Infinite
@@ -446,49 +446,20 @@ impl f16 {
446
446
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
447
447
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
448
448
// Most of std's targets don't use those, but they are used for thumbv7neon.
449
- // 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 ,
477
- }
478
- }
479
-
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.
483
- #[ inline]
484
- #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
485
- const fn classify_bits ( b : u16 ) -> FpCategory {
486
- match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
487
- ( 0 , Self :: EXP_MASK ) => FpCategory :: Infinite ,
488
- ( _, Self :: EXP_MASK ) => FpCategory :: Nan ,
489
- ( 0 , 0 ) => FpCategory :: Zero ,
490
- ( _, 0 ) => FpCategory :: Subnormal ,
491
- _ => FpCategory :: Normal ,
449
+ // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
450
+ // float codegen on this hardware, this doesn't actually return a right answer for NaN
451
+ // because it cannot correctly discern between a floating point NaN, and some normal
452
+ // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
453
+ // we are fine.
454
+ // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
455
+ // like the f64 version does, but I need to run more checks on how things go on x86.
456
+ // I fear losing mantissa data that would have answered that differently.
457
+ let b = self . to_bits ( ) ;
458
+ match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
459
+ ( 0 , 0 ) => FpCategory :: Zero ,
460
+ ( _, 0 ) => FpCategory :: Subnormal ,
461
+ _ => FpCategory :: Normal ,
462
+ }
492
463
}
493
464
}
494
465
@@ -952,48 +923,7 @@ impl f16 {
952
923
#[ must_use = "this returns the result of the operation, without modifying the original" ]
953
924
pub const fn to_bits ( self ) -> u16 {
954
925
// SAFETY: `u16` is a plain old datatype so we can always transmute to it.
955
- // ...sorta.
956
- //
957
- // It turns out that at runtime, it is possible for a floating point number
958
- // to be subject to a floating point mode that alters nonzero subnormal numbers
959
- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
960
- //
961
- // And, of course evaluating to a NaN value is fairly nondeterministic.
962
- // More precisely: when NaN should be returned is knowable, but which NaN?
963
- // So far that's defined by a combination of LLVM and the CPU, not Rust.
964
- // This function, however, allows observing the bitstring of a NaN,
965
- // thus introspection on CTFE.
966
- //
967
- // In order to preserve, at least for the moment, const-to-runtime equivalence,
968
- // we reject any of these possible situations from happening.
969
- #[ inline]
970
- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
971
- const fn ct_f16_to_u16 ( ct : f16 ) -> u16 {
972
- // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
973
- // want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
974
- // convention discrepancies calling intrinsics). So just classify the bits instead.
975
-
976
- // SAFETY: this is a POD transmutation
977
- let bits = unsafe { mem:: transmute :: < f16 , u16 > ( ct) } ;
978
- match f16:: classify_bits ( bits) {
979
- FpCategory :: Nan => {
980
- panic ! ( "const-eval error: cannot use f16::to_bits on a NaN" )
981
- }
982
- FpCategory :: Subnormal => {
983
- panic ! ( "const-eval error: cannot use f16::to_bits on a subnormal number" )
984
- }
985
- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => bits,
986
- }
987
- }
988
-
989
- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
990
- fn rt_f16_to_u16 ( x : f16 ) -> u16 {
991
- // SAFETY: `u16` is a plain old datatype so we can always... uh...
992
- // ...look, just pretend you forgot what you just read.
993
- // Stability concerns.
994
- unsafe { mem:: transmute ( x) }
995
- }
996
- intrinsics:: const_eval_select ( ( self , ) , ct_f16_to_u16, rt_f16_to_u16)
926
+ unsafe { mem:: transmute ( self ) }
997
927
}
998
928
999
929
/// Raw transmutation from `u16`.
@@ -1040,49 +970,8 @@ impl f16 {
1040
970
#[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1041
971
pub const fn from_bits ( v : u16 ) -> Self {
1042
972
// It turns out the safety issues with sNaN were overblown! Hooray!
1043
- // SAFETY: `u16` is a plain old datatype so we can always transmute from it
1044
- // ...sorta.
1045
- //
1046
- // It turns out that at runtime, it is possible for a floating point number
1047
- // to be subject to floating point modes that alter nonzero subnormal numbers
1048
- // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1049
- // This is not a problem usually, but at least one tier2 platform for Rust
1050
- // actually exhibits this behavior by default: thumbv7neon
1051
- // aka "the Neon FPU in AArch32 state"
1052
- //
1053
- // And, of course evaluating to a NaN value is fairly nondeterministic.
1054
- // More precisely: when NaN should be returned is knowable, but which NaN?
1055
- // So far that's defined by a combination of LLVM and the CPU, not Rust.
1056
- // This function, however, allows observing the bitstring of a NaN,
1057
- // thus introspection on CTFE.
1058
- //
1059
- // In order to preserve, at least for the moment, const-to-runtime equivalence,
1060
- // reject any of these possible situations from happening.
1061
- #[ inline]
1062
- #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1063
- const fn ct_u16_to_f16 ( ct : u16 ) -> f16 {
1064
- match f16:: classify_bits ( ct) {
1065
- FpCategory :: Subnormal => {
1066
- panic ! ( "const-eval error: cannot use f16::from_bits on a subnormal number" )
1067
- }
1068
- FpCategory :: Nan => {
1069
- panic ! ( "const-eval error: cannot use f16::from_bits on NaN" )
1070
- }
1071
- FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
1072
- // SAFETY: It's not a frumious number
1073
- unsafe { mem:: transmute :: < u16 , f16 > ( ct) }
1074
- }
1075
- }
1076
- }
1077
-
1078
- #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
1079
- fn rt_u16_to_f16 ( x : u16 ) -> f16 {
1080
- // SAFETY: `u16` is a plain old datatype so we can always... uh...
1081
- // ...look, just pretend you forgot what you just read.
1082
- // Stability concerns.
1083
- unsafe { mem:: transmute ( x) }
1084
- }
1085
- intrinsics:: const_eval_select ( ( v, ) , ct_u16_to_f16, rt_u16_to_f16)
973
+ // SAFETY: `u16` is a plain old datatype so we can always transmute from it.
974
+ unsafe { mem:: transmute ( v) }
1086
975
}
1087
976
1088
977
/// Returns the memory representation of this floating point number as a byte array in
0 commit comments