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