12
12
#![ unstable( feature = "f16" , issue = "116909" ) ]
13
13
14
14
use crate :: convert:: FloatToInt ;
15
+ #[ cfg( not( test) ) ]
16
+ use crate :: intrinsics;
15
17
use crate :: mem;
16
18
use crate :: num:: FpCategory ;
17
19
@@ -788,12 +790,51 @@ impl f16 {
788
790
/// ```
789
791
#[ inline]
790
792
#[ unstable( feature = "f16" , issue = "116909" ) ]
793
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
791
794
#[ must_use = "this returns the result of the operation, without modifying the original" ]
792
- pub fn to_bits ( self ) -> u16 {
793
- // SAFETY: `u16` is a plain old datatype so we can always... uh...
794
- // ...look, just pretend you forgot what you just read.
795
- // Stability concerns.
796
- unsafe { mem:: transmute ( self ) }
795
+ pub const fn to_bits ( self ) -> u16 {
796
+ // SAFETY: `u16` is a plain old datatype so we can always transmute to it.
797
+ // ...sorta.
798
+ //
799
+ // It turns out that at runtime, it is possible for a floating point number
800
+ // to be subject to a floating point mode that alters nonzero subnormal numbers
801
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
802
+ //
803
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
804
+ // More precisely: when NaN should be returned is knowable, but which NaN?
805
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
806
+ // This function, however, allows observing the bitstring of a NaN,
807
+ // thus introspection on CTFE.
808
+ //
809
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
810
+ // we reject any of these possible situations from happening.
811
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
812
+ const fn ct_f16_to_u16 ( ct : f16 ) -> u16 {
813
+ // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
814
+ // want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
815
+ // convention discrepancies calling intrinsics). So just classify the bits instead.
816
+
817
+ // SAFETY: this is a POD transmutation
818
+ let bits = unsafe { mem:: transmute :: < f16 , u16 > ( ct) } ;
819
+ match f16:: classify_bits ( bits) {
820
+ FpCategory :: Nan => {
821
+ panic ! ( "const-eval error: cannot use f16::to_bits on a NaN" )
822
+ }
823
+ FpCategory :: Subnormal => {
824
+ panic ! ( "const-eval error: cannot use f16::to_bits on a subnormal number" )
825
+ }
826
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => bits,
827
+ }
828
+ }
829
+
830
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
831
+ fn rt_f16_to_u16 ( x : f16 ) -> u16 {
832
+ // SAFETY: `u16` is a plain old datatype so we can always... uh...
833
+ // ...look, just pretend you forgot what you just read.
834
+ // Stability concerns.
835
+ unsafe { mem:: transmute ( x) }
836
+ }
837
+ intrinsics:: const_eval_select ( ( self , ) , ct_f16_to_u16, rt_f16_to_u16)
797
838
}
798
839
799
840
/// Raw transmutation from `u16`.
@@ -837,11 +878,51 @@ impl f16 {
837
878
#[ inline]
838
879
#[ must_use]
839
880
#[ unstable( feature = "f16" , issue = "116909" ) ]
840
- pub fn from_bits ( v : u16 ) -> Self {
841
- // SAFETY: `u16` is a plain old datatype so we can always... uh...
842
- // ...look, just pretend you forgot what you just read.
843
- // Stability concerns.
844
- unsafe { mem:: transmute ( v) }
881
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
882
+ pub const fn from_bits ( v : u16 ) -> Self {
883
+ // It turns out the safety issues with sNaN were overblown! Hooray!
884
+ // SAFETY: `u16` is a plain old datatype so we can always transmute from it
885
+ // ...sorta.
886
+ //
887
+ // It turns out that at runtime, it is possible for a floating point number
888
+ // to be subject to floating point modes that alter nonzero subnormal numbers
889
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
890
+ // This is not a problem usually, but at least one tier2 platform for Rust
891
+ // actually exhibits this behavior by default: thumbv7neon
892
+ // aka "the Neon FPU in AArch32 state"
893
+ //
894
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
895
+ // More precisely: when NaN should be returned is knowable, but which NaN?
896
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
897
+ // This function, however, allows observing the bitstring of a NaN,
898
+ // thus introspection on CTFE.
899
+ //
900
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
901
+ // reject any of these possible situations from happening.
902
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
903
+ const fn ct_u16_to_f16 ( ct : u16 ) -> f16 {
904
+ match f16:: classify_bits ( ct) {
905
+ FpCategory :: Subnormal => {
906
+ panic ! ( "const-eval error: cannot use f16::from_bits on a subnormal number" )
907
+ }
908
+ FpCategory :: Nan => {
909
+ panic ! ( "const-eval error: cannot use f16::from_bits on NaN" )
910
+ }
911
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
912
+ // SAFETY: It's not a frumious number
913
+ unsafe { mem:: transmute :: < u16 , f16 > ( ct) }
914
+ }
915
+ }
916
+ }
917
+
918
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
919
+ fn rt_u16_to_f16 ( x : u16 ) -> f16 {
920
+ // SAFETY: `u16` is a plain old datatype so we can always... uh...
921
+ // ...look, just pretend you forgot what you just read.
922
+ // Stability concerns.
923
+ unsafe { mem:: transmute ( x) }
924
+ }
925
+ intrinsics:: const_eval_select ( ( v, ) , ct_u16_to_f16, rt_u16_to_f16)
845
926
}
846
927
847
928
/// Return the memory representation of this floating point number as a byte array in
@@ -860,8 +941,9 @@ impl f16 {
860
941
/// ```
861
942
#[ inline]
862
943
#[ unstable( feature = "f16" , issue = "116909" ) ]
944
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
863
945
#[ must_use = "this returns the result of the operation, without modifying the original" ]
864
- pub fn to_be_bytes ( self ) -> [ u8 ; 2 ] {
946
+ pub const fn to_be_bytes ( self ) -> [ u8 ; 2 ] {
865
947
self . to_bits ( ) . to_be_bytes ( )
866
948
}
867
949
@@ -881,8 +963,9 @@ impl f16 {
881
963
/// ```
882
964
#[ inline]
883
965
#[ unstable( feature = "f16" , issue = "116909" ) ]
966
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
884
967
#[ must_use = "this returns the result of the operation, without modifying the original" ]
885
- pub fn to_le_bytes ( self ) -> [ u8 ; 2 ] {
968
+ pub const fn to_le_bytes ( self ) -> [ u8 ; 2 ] {
886
969
self . to_bits ( ) . to_le_bytes ( )
887
970
}
888
971
@@ -915,8 +998,9 @@ impl f16 {
915
998
/// ```
916
999
#[ inline]
917
1000
#[ unstable( feature = "f16" , issue = "116909" ) ]
1001
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
918
1002
#[ must_use = "this returns the result of the operation, without modifying the original" ]
919
- pub fn to_ne_bytes ( self ) -> [ u8 ; 2 ] {
1003
+ pub const fn to_ne_bytes ( self ) -> [ u8 ; 2 ] {
920
1004
self . to_bits ( ) . to_ne_bytes ( )
921
1005
}
922
1006
@@ -938,7 +1022,8 @@ impl f16 {
938
1022
#[ inline]
939
1023
#[ must_use]
940
1024
#[ unstable( feature = "f16" , issue = "116909" ) ]
941
- pub fn from_be_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1025
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1026
+ pub const fn from_be_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
942
1027
Self :: from_bits ( u16:: from_be_bytes ( bytes) )
943
1028
}
944
1029
@@ -960,7 +1045,8 @@ impl f16 {
960
1045
#[ inline]
961
1046
#[ must_use]
962
1047
#[ unstable( feature = "f16" , issue = "116909" ) ]
963
- pub fn from_le_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1048
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1049
+ pub const fn from_le_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
964
1050
Self :: from_bits ( u16:: from_le_bytes ( bytes) )
965
1051
}
966
1052
@@ -993,7 +1079,8 @@ impl f16 {
993
1079
#[ inline]
994
1080
#[ must_use]
995
1081
#[ unstable( feature = "f16" , issue = "116909" ) ]
996
- pub fn from_ne_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1082
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1083
+ pub const fn from_ne_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
997
1084
Self :: from_bits ( u16:: from_ne_bytes ( bytes) )
998
1085
}
999
1086
0 commit comments