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
@@ -758,12 +760,51 @@ impl f128 {
758
760
/// ```
759
761
#[ inline]
760
762
#[ unstable( feature = "f128" , issue = "116909" ) ]
763
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
761
764
#[ must_use = "this returns the result of the operation, without modifying the original" ]
762
- pub fn to_bits ( self ) -> u128 {
763
- // SAFETY: `u128` is a plain old datatype so we can always... uh...
764
- // ...look, just pretend you forgot what you just read.
765
- // Stability concerns.
766
- unsafe { mem:: transmute ( self ) }
765
+ pub const fn to_bits ( self ) -> u128 {
766
+ // SAFETY: `u128` is a plain old datatype so we can always transmute to it.
767
+ // ...sorta.
768
+ //
769
+ // It turns out that at runtime, it is possible for a floating point number
770
+ // to be subject to a floating point mode that alters nonzero subnormal numbers
771
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
772
+ //
773
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
774
+ // More precisely: when NaN should be returned is knowable, but which NaN?
775
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
776
+ // This function, however, allows observing the bitstring of a NaN,
777
+ // thus introspection on CTFE.
778
+ //
779
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
780
+ // we reject any of these possible situations from happening.
781
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
782
+ const fn ct_f128_to_u128 ( ct : f128 ) -> u128 {
783
+ // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
784
+ // is not available on all platforms (needs `netf2` and `unordtf2`). So classify
785
+ // the bits instead.
786
+
787
+ // SAFETY: this is a POD transmutation
788
+ let bits = unsafe { mem:: transmute :: < f128 , u128 > ( ct) } ;
789
+ match f128:: classify_bits ( bits) {
790
+ FpCategory :: Nan => {
791
+ panic ! ( "const-eval error: cannot use f128::to_bits on a NaN" )
792
+ }
793
+ FpCategory :: Subnormal => {
794
+ panic ! ( "const-eval error: cannot use f128::to_bits on a subnormal number" )
795
+ }
796
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => bits,
797
+ }
798
+ }
799
+
800
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
801
+ fn rt_f128_to_u128 ( x : f128 ) -> u128 {
802
+ // SAFETY: `u128` is a plain old datatype so we can always... uh...
803
+ // ...look, just pretend you forgot what you just read.
804
+ // Stability concerns.
805
+ unsafe { mem:: transmute ( x) }
806
+ }
807
+ intrinsics:: const_eval_select ( ( self , ) , ct_f128_to_u128, rt_f128_to_u128)
767
808
}
768
809
769
810
/// Raw transmutation from `u128`.
@@ -808,11 +849,51 @@ impl f128 {
808
849
#[ inline]
809
850
#[ must_use]
810
851
#[ unstable( feature = "f128" , issue = "116909" ) ]
811
- pub fn from_bits ( v : u128 ) -> Self {
812
- // SAFETY: `u128 is a plain old datatype so we can always... uh...
813
- // ...look, just pretend you forgot what you just read.
814
- // Stability concerns.
815
- unsafe { mem:: transmute ( v) }
852
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
853
+ pub const fn from_bits ( v : u128 ) -> Self {
854
+ // It turns out the safety issues with sNaN were overblown! Hooray!
855
+ // SAFETY: `u128` is a plain old datatype so we can always transmute from it
856
+ // ...sorta.
857
+ //
858
+ // It turns out that at runtime, it is possible for a floating point number
859
+ // to be subject to floating point modes that alter nonzero subnormal numbers
860
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
861
+ // This is not a problem usually, but at least one tier2 platform for Rust
862
+ // actually exhibits this behavior by default: thumbv7neon
863
+ // aka "the Neon FPU in AArch32 state"
864
+ //
865
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
866
+ // More precisely: when NaN should be returned is knowable, but which NaN?
867
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
868
+ // This function, however, allows observing the bitstring of a NaN,
869
+ // thus introspection on CTFE.
870
+ //
871
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
872
+ // reject any of these possible situations from happening.
873
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
874
+ const fn ct_u128_to_f128 ( ct : u128 ) -> f128 {
875
+ match f128:: classify_bits ( ct) {
876
+ FpCategory :: Subnormal => {
877
+ panic ! ( "const-eval error: cannot use f128::from_bits on a subnormal number" )
878
+ }
879
+ FpCategory :: Nan => {
880
+ panic ! ( "const-eval error: cannot use f128::from_bits on NaN" )
881
+ }
882
+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
883
+ // SAFETY: It's not a frumious number
884
+ unsafe { mem:: transmute :: < u128 , f128 > ( ct) }
885
+ }
886
+ }
887
+ }
888
+
889
+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
890
+ fn rt_u128_to_f128 ( x : u128 ) -> f128 {
891
+ // SAFETY: `u128` is a plain old datatype so we can always... uh...
892
+ // ...look, just pretend you forgot what you just read.
893
+ // Stability concerns.
894
+ unsafe { mem:: transmute ( x) }
895
+ }
896
+ intrinsics:: const_eval_select ( ( v, ) , ct_u128_to_f128, rt_u128_to_f128)
816
897
}
817
898
818
899
/// Return the memory representation of this floating point number as a byte array in
@@ -835,8 +916,9 @@ impl f128 {
835
916
/// ```
836
917
#[ inline]
837
918
#[ unstable( feature = "f128" , issue = "116909" ) ]
919
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
838
920
#[ must_use = "this returns the result of the operation, without modifying the original" ]
839
- pub fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
921
+ pub const fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
840
922
self . to_bits ( ) . to_be_bytes ( )
841
923
}
842
924
@@ -860,8 +942,9 @@ impl f128 {
860
942
/// ```
861
943
#[ inline]
862
944
#[ unstable( feature = "f128" , issue = "116909" ) ]
945
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
863
946
#[ must_use = "this returns the result of the operation, without modifying the original" ]
864
- pub fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
947
+ pub const fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
865
948
self . to_bits ( ) . to_le_bytes ( )
866
949
}
867
950
@@ -896,8 +979,9 @@ impl f128 {
896
979
/// ```
897
980
#[ inline]
898
981
#[ unstable( feature = "f128" , issue = "116909" ) ]
982
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
899
983
#[ must_use = "this returns the result of the operation, without modifying the original" ]
900
- pub fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
984
+ pub const fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
901
985
self . to_bits ( ) . to_ne_bytes ( )
902
986
}
903
987
@@ -923,7 +1007,8 @@ impl f128 {
923
1007
#[ inline]
924
1008
#[ must_use]
925
1009
#[ unstable( feature = "f128" , issue = "116909" ) ]
926
- pub fn from_be_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1010
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1011
+ pub const fn from_be_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
927
1012
Self :: from_bits ( u128:: from_be_bytes ( bytes) )
928
1013
}
929
1014
@@ -949,7 +1034,8 @@ impl f128 {
949
1034
#[ inline]
950
1035
#[ must_use]
951
1036
#[ unstable( feature = "f128" , issue = "116909" ) ]
952
- pub fn from_le_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1037
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1038
+ pub const fn from_le_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
953
1039
Self :: from_bits ( u128:: from_le_bytes ( bytes) )
954
1040
}
955
1041
@@ -985,7 +1071,8 @@ impl f128 {
985
1071
#[ inline]
986
1072
#[ must_use]
987
1073
#[ unstable( feature = "f128" , issue = "116909" ) ]
988
- pub fn from_ne_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
1074
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1075
+ pub const fn from_ne_bytes ( bytes : [ u8 ; 16 ] ) -> Self {
989
1076
Self :: from_bits ( u128:: from_ne_bytes ( bytes) )
990
1077
}
991
1078
0 commit comments