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,51 @@ 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+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
904+ const fn ct_u16_to_f16 ( ct : u16 ) -> f16 {
905+ match f16:: classify_bits ( ct) {
906+ FpCategory :: Subnormal => {
907+ panic ! ( "const-eval error: cannot use f16::from_bits on a subnormal number" )
908+ }
909+ FpCategory :: Nan => {
910+ panic ! ( "const-eval error: cannot use f16::from_bits on NaN" )
911+ }
912+ FpCategory :: Infinite | FpCategory :: Normal | FpCategory :: Zero => {
913+ // SAFETY: It's not a frumious number
914+ unsafe { mem:: transmute :: < u16 , f16 > ( ct) }
915+ }
916+ }
917+ }
918+
919+ #[ inline( always) ] // See https://github.com/rust-lang/compiler-builtins/issues/491
920+ fn rt_u16_to_f16 ( x : u16 ) -> f16 {
921+ // SAFETY: `u16` is a plain old datatype so we can always... uh...
922+ // ...look, just pretend you forgot what you just read.
923+ // Stability concerns.
924+ unsafe { mem:: transmute ( x) }
925+ }
926+ intrinsics:: const_eval_select ( ( v, ) , ct_u16_to_f16, rt_u16_to_f16)
845927 }
846928
847929 /// Return the memory representation of this floating point number as a byte array in
@@ -860,8 +942,9 @@ impl f16 {
860942 /// ```
861943 #[ inline]
862944 #[ unstable( feature = "f16" , issue = "116909" ) ]
945+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
863946 #[ must_use = "this returns the result of the operation, without modifying the original" ]
864- pub fn to_be_bytes ( self ) -> [ u8 ; 2 ] {
947+ pub const fn to_be_bytes ( self ) -> [ u8 ; 2 ] {
865948 self . to_bits ( ) . to_be_bytes ( )
866949 }
867950
@@ -881,8 +964,9 @@ impl f16 {
881964 /// ```
882965 #[ inline]
883966 #[ unstable( feature = "f16" , issue = "116909" ) ]
967+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
884968 #[ must_use = "this returns the result of the operation, without modifying the original" ]
885- pub fn to_le_bytes ( self ) -> [ u8 ; 2 ] {
969+ pub const fn to_le_bytes ( self ) -> [ u8 ; 2 ] {
886970 self . to_bits ( ) . to_le_bytes ( )
887971 }
888972
@@ -915,8 +999,9 @@ impl f16 {
915999 /// ```
9161000 #[ inline]
9171001 #[ unstable( feature = "f16" , issue = "116909" ) ]
1002+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
9181003 #[ must_use = "this returns the result of the operation, without modifying the original" ]
919- pub fn to_ne_bytes ( self ) -> [ u8 ; 2 ] {
1004+ pub const fn to_ne_bytes ( self ) -> [ u8 ; 2 ] {
9201005 self . to_bits ( ) . to_ne_bytes ( )
9211006 }
9221007
@@ -938,7 +1023,8 @@ impl f16 {
9381023 #[ inline]
9391024 #[ must_use]
9401025 #[ unstable( feature = "f16" , issue = "116909" ) ]
941- pub fn from_be_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1026+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1027+ pub const fn from_be_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
9421028 Self :: from_bits ( u16:: from_be_bytes ( bytes) )
9431029 }
9441030
@@ -960,7 +1046,8 @@ impl f16 {
9601046 #[ inline]
9611047 #[ must_use]
9621048 #[ unstable( feature = "f16" , issue = "116909" ) ]
963- pub fn from_le_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1049+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1050+ pub const fn from_le_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
9641051 Self :: from_bits ( u16:: from_le_bytes ( bytes) )
9651052 }
9661053
@@ -993,7 +1080,8 @@ impl f16 {
9931080 #[ inline]
9941081 #[ must_use]
9951082 #[ unstable( feature = "f16" , issue = "116909" ) ]
996- pub fn from_ne_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
1083+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
1084+ pub const fn from_ne_bytes ( bytes : [ u8 ; 2 ] ) -> Self {
9971085 Self :: from_bits ( u16:: from_ne_bytes ( bytes) )
9981086 }
9991087
0 commit comments