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