1212#![ unstable( feature = "f16" , issue = "116909" ) ]
1313
1414use crate :: convert:: FloatToInt ;
15+ #[ cfg( not( test) ) ]
16+ use crate :: intrinsics;
1517use crate :: mem;
1618use crate :: num:: FpCategory ;
1719
@@ -788,12 +790,52 @@ impl f16 {
788790 /// ```
789791 #[ inline]
790792 #[ unstable( feature = "f16" , issue = "116909" ) ]
793+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
791794 #[ must_use = "this returns the result of the operation, without modifying the original" ]
792- pub fn to_bits ( self ) -> u16 {
793- // SAFETY: `u16` is a plain old datatype so we can always... uh...
794- // ...look, just pretend you forgot what you just read.
795- // Stability concerns.
796- unsafe { mem:: transmute ( self ) }
795+ pub const fn to_bits ( self ) -> u16 {
796+ // SAFETY: `u16` is a plain old datatype so we can always transmute to it.
797+ // ...sorta.
798+ //
799+ // It turns out that at runtime, it is possible for a floating point number
800+ // to be subject to a floating point mode that alters nonzero subnormal numbers
801+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
802+ //
803+ // And, of course evaluating to a NaN value is fairly nondeterministic.
804+ // More precisely: when NaN should be returned is knowable, but which NaN?
805+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
806+ // This function, however, allows observing the bitstring of a NaN,
807+ // thus introspection on CTFE.
808+ //
809+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
810+ // we reject any of these possible situations from happening.
811+ #[ inline]
812+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
813+ const fn ct_f16_to_u16 ( ct : f16 ) -> u16 {
814+ // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
815+ // want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
816+ // convention discrepancies calling intrinsics). So just classify the bits instead.
817+
818+ // SAFETY: this is a POD transmutation
819+ let bits = unsafe { mem:: transmute :: < f16 , u16 > ( ct) } ;
820+ match f16:: classify_bits ( bits) {
821+ FpCategory :: Nan => {
822+ panic ! ( "const-eval error: cannot use f16::to_bits on a NaN" )
823+ }
824+ FpCategory :: Subnormal => {
825+ panic ! ( "const-eval error: cannot use f16::to_bits on a subnormal number" )
826+ }
827+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => bits,
828+ }
829+ }
830+
831+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
832+ fn rt_f16_to_u16 ( x : f16 ) -> u16 {
833+ // SAFETY: `u16` is a plain old datatype so we can always... uh...
834+ // ...look, just pretend you forgot what you just read.
835+ // Stability concerns.
836+ unsafe { mem:: transmute ( x) }
837+ }
838+ intrinsics:: const_eval_select ( ( self , ) , ct_f16_to_u16, rt_f16_to_u16)
797839 }
798840
799841 /// Raw transmutation from `u16`.
@@ -837,11 +879,52 @@ impl f16 {
837879 #[ inline]
838880 #[ must_use]
839881 #[ unstable( feature = "f16" , issue = "116909" ) ]
840- pub fn from_bits ( v : u16 ) -> Self {
841- // SAFETY: `u16` is a plain old datatype so we can always... uh...
842- // ...look, just pretend you forgot what you just read.
843- // Stability concerns.
844- unsafe { mem:: transmute ( v) }
882+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
883+ pub const fn from_bits ( v : u16 ) -> Self {
884+ // It turns out the safety issues with sNaN were overblown! Hooray!
885+ // SAFETY: `u16` is a plain old datatype so we can always transmute from it
886+ // ...sorta.
887+ //
888+ // It turns out that at runtime, it is possible for a floating point number
889+ // to be subject to floating point modes that alter nonzero subnormal numbers
890+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
891+ // This is not a problem usually, but at least one tier2 platform for Rust
892+ // actually exhibits this behavior by default: thumbv7neon
893+ // aka "the Neon FPU in AArch32 state"
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+ #[ inline]
904+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
905+ const fn ct_u16_to_f16 ( ct : u16 ) -> f16 {
906+ match f16:: classify_bits ( ct) {
907+ FpCategory :: Subnormal => {
908+ panic ! ( "const-eval error: cannot use f16::from_bits on a subnormal number" )
909+ }
910+ FpCategory :: Nan => {
911+ panic ! ( "const-eval error: cannot use f16::from_bits on NaN" )
912+ }
913+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
914+ // SAFETY: It's not a frumious number
915+ unsafe { mem:: transmute :: < u16 , f16 > ( ct) }
916+ }
917+ }
918+ }
919+
920+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
921+ fn rt_u16_to_f16 ( x : u16 ) -> f16 {
922+ // SAFETY: `u16` is a plain old datatype so we can always... uh...
923+ // ...look, just pretend you forgot what you just read.
924+ // Stability concerns.
925+ unsafe { mem:: transmute ( x) }
926+ }
927+ intrinsics:: const_eval_select ( ( v, ) , ct_u16_to_f16, rt_u16_to_f16)
845928 }
846929
847930 /// Return the memory representation of this floating point number as a byte array in
@@ -860,8 +943,9 @@ impl f16 {
860943 /// ```
861944 #[ inline]
862945 #[ unstable( feature = "f16" , 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_be_bytes ( self ) -> [ u8 ; 2 ] {
948+ pub const fn to_be_bytes ( self ) -> [ u8 ; 2 ] {
865949 self . to_bits ( ) . to_be_bytes ( )
866950 }
867951
@@ -881,8 +965,9 @@ impl f16 {
881965 /// ```
882966 #[ inline]
883967 #[ unstable( feature = "f16" , issue = "116909" ) ]
968+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
884969 #[ must_use = "this returns the result of the operation, without modifying the original" ]
885- pub fn to_le_bytes ( self ) -> [ u8 ; 2 ] {
970+ pub const fn to_le_bytes ( self ) -> [ u8 ; 2 ] {
886971 self . to_bits ( ) . to_le_bytes ( )
887972 }
888973
@@ -915,8 +1000,9 @@ impl f16 {
9151000 /// ```
9161001 #[ inline]
9171002 #[ unstable( feature = "f16" , issue = "116909" ) ]
1003+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
9181004 #[ must_use = "this returns the result of the operation, without modifying the original" ]
919- pub fn to_ne_bytes ( self ) -> [ u8 ; 2 ] {
1005+ pub const fn to_ne_bytes ( self ) -> [ u8 ; 2 ] {
9201006 self . to_bits ( ) . to_ne_bytes ( )
9211007 }
9221008
@@ -938,7 +1024,8 @@ impl f16 {
9381024 #[ inline]
9391025 #[ must_use]
9401026 #[ unstable( feature = "f16" , issue = "116909" ) ]
941- pub fn from_be_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1027+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1028+ pub const fn from_be_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
9421029 Self :: from_bits ( u16:: from_be_bytes ( bytes) )
9431030 }
9441031
@@ -960,7 +1047,8 @@ impl f16 {
9601047 #[ inline]
9611048 #[ must_use]
9621049 #[ unstable( feature = "f16" , issue = "116909" ) ]
963- pub fn from_le_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1050+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1051+ pub const fn from_le_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
9641052 Self :: from_bits ( u16:: from_le_bytes ( bytes) )
9651053 }
9661054
@@ -993,7 +1081,8 @@ impl f16 {
9931081 #[ inline]
9941082 #[ must_use]
9951083 #[ unstable( feature = "f16" , issue = "116909" ) ]
996- pub fn from_ne_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1084+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1085+ pub const fn from_ne_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
9971086 Self :: from_bits ( u16:: from_ne_bytes ( bytes) )
9981087 }
9991088
0 commit comments