1212#![ unstable( feature = "f128" , issue = "116909" ) ]
1313
1414use crate :: convert:: FloatToInt ;
15+ #[ cfg( not( test) ) ]
16+ use crate :: intrinsics;
1517use crate :: mem;
1618use crate :: num:: FpCategory ;
1719
@@ -758,12 +760,52 @@ impl f128 {
758760 /// ```
759761 #[ inline]
760762 #[ unstable( feature = "f128" , issue = "116909" ) ]
763+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
761764 #[ 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)
767809 }
768810
769811 /// Raw transmutation from `u128`.
@@ -808,11 +850,51 @@ impl f128 {
808850 #[ inline]
809851 #[ must_use]
810852 #[ 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)
816898 }
817899
818900 /// Return the memory representation of this floating point number as a byte array in
@@ -835,8 +917,9 @@ impl f128 {
835917 /// ```
836918 #[ inline]
837919 #[ unstable( feature = "f128" , issue = "116909" ) ]
920+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
838921 #[ 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 ] {
840923 self . to_bits ( ) . to_be_bytes ( )
841924 }
842925
@@ -860,8 +943,9 @@ impl f128 {
860943 /// ```
861944 #[ inline]
862945 #[ unstable( feature = "f128" , issue = "116909" ) ]
946+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
863947 #[ 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 ] {
865949 self . to_bits ( ) . to_le_bytes ( )
866950 }
867951
@@ -896,8 +980,9 @@ impl f128 {
896980 /// ```
897981 #[ inline]
898982 #[ unstable( feature = "f128" , issue = "116909" ) ]
983+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
899984 #[ 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 ] {
901986 self . to_bits ( ) . to_ne_bytes ( )
902987 }
903988
@@ -923,7 +1008,8 @@ impl f128 {
9231008 #[ inline]
9241009 #[ must_use]
9251010 #[ 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 {
9271013 Self :: from_bits ( u128:: from_be_bytes ( bytes) )
9281014 }
9291015
@@ -949,7 +1035,8 @@ impl f128 {
9491035 #[ inline]
9501036 #[ must_use]
9511037 #[ 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 {
9531040 Self :: from_bits ( u128:: from_le_bytes ( bytes) )
9541041 }
9551042
@@ -985,7 +1072,8 @@ impl f128 {
9851072 #[ inline]
9861073 #[ must_use]
9871074 #[ 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 {
9891077 Self :: from_bits ( u128:: from_ne_bytes ( bytes) )
9901078 }
9911079
0 commit comments