12
12
#![ unstable( feature = "f128" , 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
@@ -808,12 +810,59 @@ impl f128 {
808
810
/// ```
809
811
#[ inline]
810
812
#[ unstable( feature = "f128" , issue = "116909" ) ]
813
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
811
814
#[ must_use = "this returns the result of the operation, without modifying the original" ]
812
- pub fn to_bits ( self ) -> u128 {
813
- // SAFETY: `u128` is a plain old datatype so we can always... uh...
814
- // ...look, just pretend you forgot what you just read.
815
- // Stability concerns.
816
- unsafe { mem:: transmute ( self ) }
815
+ pub const fn to_bits ( self ) -> u128 {
816
+ // SAFETY: `u128` is a plain old datatype so we can always transmute to it.
817
+ // ...sorta.
818
+ //
819
+ // It turns out that at runtime, it is possible for a floating point number
820
+ // to be subject to a floating point mode that alters nonzero subnormal numbers
821
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
822
+ // This is not a problem per se, but at least one tier2 platform for Rust
823
+ // actually exhibits this behavior by default.
824
+ //
825
+ // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
826
+ // i.e. not soft-float, the way Rust does parameter passing can actually alter
827
+ // a number that is "not infinity" to have the same exponent as infinity,
828
+ // in a slightly unpredictable manner.
829
+ //
830
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
831
+ // More precisely: when NaN should be returned is knowable, but which NaN?
832
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
833
+ // This function, however, allows observing the bitstring of a NaN,
834
+ // thus introspection on CTFE.
835
+ //
836
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
837
+ // we reject any of these possible situations from happening.
838
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
839
+ const fn ct_f128_to_u128 ( ct : f128 ) -> u128 {
840
+ // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
841
+ // is not avaialble on all platforms (needs `netf2` and `unordtf2`). So classify
842
+ // the bits instead.
843
+
844
+ // SAFETY: this direction is a POD transmutation. We just can't return it unless
845
+ // it is normal, infinite, or zero.
846
+ let bits = unsafe { mem:: transmute :: < f128 , u128 > ( ct) } ;
847
+ match f128:: classify_bits ( bits) {
848
+ FpCategory :: Nan => {
849
+ panic ! ( "const-eval error: cannot use f128::to_bits on a NaN" )
850
+ }
851
+ FpCategory :: Subnormal => {
852
+ panic ! ( "const-eval error: cannot use f128::to_bits on a subnormal number" )
853
+ }
854
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => bits,
855
+ }
856
+ }
857
+
858
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
859
+ fn rt_f128_to_u128 ( x : f128 ) -> u128 {
860
+ // SAFETY: `u128` is a plain old datatype so we can always... uh...
861
+ // ...look, just pretend you forgot what you just read.
862
+ // Stability concerns.
863
+ unsafe { mem:: transmute ( x) }
864
+ }
865
+ intrinsics:: const_eval_select ( ( self , ) , ct_f128_to_u128, rt_f128_to_u128)
817
866
}
818
867
819
868
/// Raw transmutation from `u128`.
@@ -858,11 +907,56 @@ impl f128 {
858
907
#[ inline]
859
908
#[ must_use]
860
909
#[ unstable( feature = "f128" , issue = "116909" ) ]
861
- pub fn from_bits ( v : u128 ) -> Self {
862
- // SAFETY: `u128 is a plain old datatype so we can always... uh...
863
- // ...look, just pretend you forgot what you just read.
864
- // Stability concerns.
865
- unsafe { mem:: transmute ( v) }
910
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
911
+ pub const fn from_bits ( v : u128 ) -> Self {
912
+ // It turns out the safety issues with sNaN were overblown! Hooray!
913
+ // SAFETY: `u128` is a plain old datatype so we can always transmute from it
914
+ // ...sorta.
915
+ //
916
+ // It turns out that at runtime, it is possible for a floating point number
917
+ // to be subject to floating point modes that alter nonzero subnormal numbers
918
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
919
+ // This is not a problem usually, but at least one tier2 platform for Rust
920
+ // actually exhibits this behavior by default: thumbv7neon
921
+ // aka "the Neon FPU in AArch32 state"
922
+ //
923
+ // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
924
+ // i.e. not soft-float, the way Rust does parameter passing can actually alter
925
+ // a number that is "not infinity" to have the same exponent as infinity,
926
+ // in a slightly unpredictable manner.
927
+ //
928
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
929
+ // More precisely: when NaN should be returned is knowable, but which NaN?
930
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
931
+ // This function, however, allows observing the bitstring of a NaN,
932
+ // thus introspection on CTFE.
933
+ //
934
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
935
+ // reject any of these possible situations from happening.
936
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
937
+ const fn ct_u128_to_f128 ( ct : u128 ) -> f128 {
938
+ match f128:: classify_bits ( ct) {
939
+ FpCategory :: Subnormal => {
940
+ panic ! ( "const-eval error: cannot use f128::from_bits on a subnormal number" )
941
+ }
942
+ FpCategory :: Nan => {
943
+ panic ! ( "const-eval error: cannot use f128::from_bits on NaN" )
944
+ }
945
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
946
+ // SAFETY: It's not a frumious number
947
+ unsafe { mem:: transmute :: < u128 , f128 > ( ct) }
948
+ }
949
+ }
950
+ }
951
+
952
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
953
+ fn rt_u128_to_f128 ( x : u128 ) -> f128 {
954
+ // SAFETY: `u128` is a plain old datatype so we can always... uh...
955
+ // ...look, just pretend you forgot what you just read.
956
+ // Stability concerns.
957
+ unsafe { mem:: transmute ( x) }
958
+ }
959
+ intrinsics:: const_eval_select ( ( v, ) , ct_u128_to_f128, rt_u128_to_f128)
866
960
}
867
961
868
962
/// Return the memory representation of this floating point number as a byte array in
@@ -885,8 +979,9 @@ impl f128 {
885
979
/// ```
886
980
#[ inline]
887
981
#[ unstable( feature = "f128" , issue = "116909" ) ]
982
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
888
983
#[ must_use = "this returns the result of the operation, without modifying the original" ]
889
- pub fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
984
+ pub const fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
890
985
self . to_bits ( ) . to_be_bytes ( )
891
986
}
892
987
@@ -910,8 +1005,9 @@ impl f128 {
910
1005
/// ```
911
1006
#[ inline]
912
1007
#[ unstable( feature = "f128" , issue = "116909" ) ]
1008
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
913
1009
#[ must_use = "this returns the result of the operation, without modifying the original" ]
914
- pub fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
1010
+ pub const fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
915
1011
self . to_bits ( ) . to_le_bytes ( )
916
1012
}
917
1013
@@ -946,8 +1042,9 @@ impl f128 {
946
1042
/// ```
947
1043
#[ inline]
948
1044
#[ unstable( feature = "f128" , issue = "116909" ) ]
1045
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
949
1046
#[ must_use = "this returns the result of the operation, without modifying the original" ]
950
- pub fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
1047
+ pub const fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
951
1048
self . to_bits ( ) . to_ne_bytes ( )
952
1049
}
953
1050
@@ -973,7 +1070,8 @@ impl f128 {
973
1070
#[ inline]
974
1071
#[ must_use]
975
1072
#[ unstable( feature = "f128" , issue = "116909" ) ]
976
- pub fn from_be_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1073
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1074
+ pub const fn from_be_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
977
1075
Self :: from_bits ( u128:: from_be_bytes ( bytes) )
978
1076
}
979
1077
@@ -999,7 +1097,8 @@ impl f128 {
999
1097
#[ inline]
1000
1098
#[ must_use]
1001
1099
#[ unstable( feature = "f128" , issue = "116909" ) ]
1002
- pub fn from_le_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1100
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1101
+ pub const fn from_le_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1003
1102
Self :: from_bits ( u128:: from_le_bytes ( bytes) )
1004
1103
}
1005
1104
@@ -1035,7 +1134,8 @@ impl f128 {
1035
1134
#[ inline]
1036
1135
#[ must_use]
1037
1136
#[ unstable( feature = "f128" , issue = "116909" ) ]
1038
- pub fn from_ne_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1137
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1138
+ pub const fn from_ne_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1039
1139
Self :: from_bits ( u128:: from_ne_bytes ( bytes) )
1040
1140
}
1041
1141
0 commit comments