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
@@ -780,12 +782,54 @@ impl f128 {
780
782
/// ```
781
783
#[ inline]
782
784
#[ unstable( feature = "f128" , issue = "116909" ) ]
785
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
783
786
#[ must_use = "this returns the result of the operation, without modifying the original" ]
784
- pub fn to_bits ( self ) -> u128 {
785
- // SAFETY: `u128` is a plain old datatype so we can always... uh...
786
- // ...look, just pretend you forgot what you just read.
787
- // Stability concerns.
788
- unsafe { mem:: transmute ( self ) }
787
+ pub const fn to_bits ( self ) -> u128 {
788
+ // SAFETY: `u128` is a plain old datatype so we can always transmute to it.
789
+ // ...sorta.
790
+ //
791
+ // It turns out that at runtime, it is possible for a floating point number
792
+ // to be subject to a floating point mode that alters nonzero subnormal numbers
793
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
794
+ // This is not a problem per se, but at least one tier2 platform for Rust
795
+ // actually exhibits this behavior by default.
796
+ //
797
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
798
+ // More precisely: when NaN should be returned is knowable, but which NaN?
799
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
800
+ // This function, however, allows observing the bitstring of a NaN,
801
+ // thus introspection on CTFE.
802
+ //
803
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
804
+ // we reject any of these possible situations from happening.
805
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
806
+ const fn ct_f128_to_u128 ( ct : f128 ) -> u128 {
807
+ // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
808
+ // is not avaialble on all platforms (needs `netf2` and `unordtf2`). So classify
809
+ // the bits instead.
810
+
811
+ // SAFETY: this direction is a POD transmutation. We just can't return it unless
812
+ // it is normal, infinite, or zero.
813
+ let bits = unsafe { mem:: transmute :: < f128 , u128 > ( ct) } ;
814
+ match f128:: classify_bits ( bits) {
815
+ FpCategory :: Nan => {
816
+ panic ! ( "const-eval error: cannot use f128::to_bits on a NaN" )
817
+ }
818
+ FpCategory :: Subnormal => {
819
+ panic ! ( "const-eval error: cannot use f128::to_bits on a subnormal number" )
820
+ }
821
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => bits,
822
+ }
823
+ }
824
+
825
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
826
+ fn rt_f128_to_u128 ( x : f128 ) -> u128 {
827
+ // SAFETY: `u128` is a plain old datatype so we can always... uh...
828
+ // ...look, just pretend you forgot what you just read.
829
+ // Stability concerns.
830
+ unsafe { mem:: transmute ( x) }
831
+ }
832
+ intrinsics:: const_eval_select ( ( self , ) , ct_f128_to_u128, rt_f128_to_u128)
789
833
}
790
834
791
835
/// Raw transmutation from `u128`.
@@ -830,11 +874,56 @@ impl f128 {
830
874
#[ inline]
831
875
#[ must_use]
832
876
#[ unstable( feature = "f128" , issue = "116909" ) ]
833
- pub fn from_bits ( v : u128 ) -> Self {
834
- // SAFETY: `u128 is a plain old datatype so we can always... uh...
835
- // ...look, just pretend you forgot what you just read.
836
- // Stability concerns.
837
- unsafe { mem:: transmute ( v) }
877
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
878
+ pub const fn from_bits ( v : u128 ) -> Self {
879
+ // It turns out the safety issues with sNaN were overblown! Hooray!
880
+ // SAFETY: `u128` is a plain old datatype so we can always transmute from it
881
+ // ...sorta.
882
+ //
883
+ // It turns out that at runtime, it is possible for a floating point number
884
+ // to be subject to floating point modes that alter nonzero subnormal numbers
885
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
886
+ // This is not a problem usually, but at least one tier2 platform for Rust
887
+ // actually exhibits this behavior by default: thumbv7neon
888
+ // aka "the Neon FPU in AArch32 state"
889
+ //
890
+ // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
891
+ // i.e. not soft-float, the way Rust does parameter passing can actually alter
892
+ // a number that is "not infinity" to have the same exponent as infinity,
893
+ // in a slightly unpredictable manner.
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
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
904
+ const fn ct_u128_to_f128 ( ct : u128 ) -> f128 {
905
+ match f128:: classify_bits ( ct) {
906
+ FpCategory :: Subnormal => {
907
+ panic ! ( "const-eval error: cannot use f128::from_bits on a subnormal number" )
908
+ }
909
+ FpCategory :: Nan => {
910
+ panic ! ( "const-eval error: cannot use f128::from_bits on NaN" )
911
+ }
912
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
913
+ // SAFETY: It's not a frumious number
914
+ unsafe { mem:: transmute :: < u128 , f128 > ( ct) }
915
+ }
916
+ }
917
+ }
918
+
919
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
920
+ fn rt_u128_to_f128 ( x : u128 ) -> f128 {
921
+ // SAFETY: `u128` is a plain old datatype so we can always... uh...
922
+ // ...look, just pretend you forgot what you just read.
923
+ // Stability concerns.
924
+ unsafe { mem:: transmute ( x) }
925
+ }
926
+ intrinsics:: const_eval_select ( ( v, ) , ct_u128_to_f128, rt_u128_to_f128)
838
927
}
839
928
840
929
/// Return the memory representation of this floating point number as a byte array in
@@ -857,8 +946,9 @@ impl f128 {
857
946
/// ```
858
947
#[ inline]
859
948
#[ unstable( feature = "f128" , issue = "116909" ) ]
949
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
860
950
#[ must_use = "this returns the result of the operation, without modifying the original" ]
861
- pub fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
951
+ pub const fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
862
952
self . to_bits ( ) . to_be_bytes ( )
863
953
}
864
954
@@ -882,8 +972,9 @@ impl f128 {
882
972
/// ```
883
973
#[ inline]
884
974
#[ unstable( feature = "f128" , issue = "116909" ) ]
975
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
885
976
#[ must_use = "this returns the result of the operation, without modifying the original" ]
886
- pub fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
977
+ pub const fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
887
978
self . to_bits ( ) . to_le_bytes ( )
888
979
}
889
980
@@ -918,8 +1009,9 @@ impl f128 {
918
1009
/// ```
919
1010
#[ inline]
920
1011
#[ unstable( feature = "f128" , issue = "116909" ) ]
1012
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
921
1013
#[ must_use = "this returns the result of the operation, without modifying the original" ]
922
- pub fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
1014
+ pub const fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
923
1015
self . to_bits ( ) . to_ne_bytes ( )
924
1016
}
925
1017
@@ -945,7 +1037,8 @@ impl f128 {
945
1037
#[ inline]
946
1038
#[ must_use]
947
1039
#[ unstable( feature = "f128" , issue = "116909" ) ]
948
- pub fn from_be_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1040
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1041
+ pub const fn from_be_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
949
1042
Self :: from_bits ( u128:: from_be_bytes ( bytes) )
950
1043
}
951
1044
@@ -971,7 +1064,8 @@ impl f128 {
971
1064
#[ inline]
972
1065
#[ must_use]
973
1066
#[ unstable( feature = "f128" , issue = "116909" ) ]
974
- pub fn from_le_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1067
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1068
+ pub const fn from_le_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
975
1069
Self :: from_bits ( u128:: from_le_bytes ( bytes) )
976
1070
}
977
1071
@@ -1007,7 +1101,8 @@ impl f128 {
1007
1101
#[ inline]
1008
1102
#[ must_use]
1009
1103
#[ unstable( feature = "f128" , issue = "116909" ) ]
1010
- pub fn from_ne_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1104
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1105
+ pub const fn from_ne_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1011
1106
Self :: from_bits ( u128:: from_ne_bytes ( bytes) )
1012
1107
}
1013
1108
0 commit comments