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