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