1212#![ unstable( feature = "f128" , issue = "116909" ) ]
1313
1414use crate :: convert:: FloatToInt ;
15+ #[ cfg( not( test) ) ]
16+ use crate :: intrinsics;
1517use crate :: mem;
18+ use crate :: num:: FpCategory ;
1619
1720/// Basic mathematical constants.
1821#[ unstable( feature = "f128" , issue = "116909" ) ]
@@ -251,6 +254,12 @@ impl f128 {
251254 #[ cfg( not( bootstrap) ) ]
252255 pub ( crate ) const SIGN_MASK : u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000 ;
253256
257+ /// Exponent mask
258+ pub ( crate ) const EXP_MASK : u128 = 0x7fff_0000_0000_0000_0000_0000_0000_0000 ;
259+
260+ /// Mantissa mask
261+ pub ( crate ) const MAN_MASK : u128 = 0x0000_ffff_ffff_ffff_ffff_ffff_ffff_ffff ;
262+
254263 /// Minimum representable positive value (min subnormal)
255264 #[ cfg( not( bootstrap) ) ]
256265 const TINY_BITS : u128 = 0x1 ;
@@ -354,6 +363,119 @@ impl f128 {
354363 self . abs_private ( ) < Self :: INFINITY
355364 }
356365
366+ /// Returns `true` if the number is [subnormal].
367+ ///
368+ /// ```
369+ /// #![feature(f128)]
370+ /// # // FIXME(f16_f128): remove when `eqtf2` is available
371+ /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
372+ ///
373+ /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128
374+ /// let max = f128::MAX;
375+ /// let lower_than_min = 1.0e-4960_f128;
376+ /// let zero = 0.0_f128;
377+ ///
378+ /// assert!(!min.is_subnormal());
379+ /// assert!(!max.is_subnormal());
380+ ///
381+ /// assert!(!zero.is_subnormal());
382+ /// assert!(!f128::NAN.is_subnormal());
383+ /// assert!(!f128::INFINITY.is_subnormal());
384+ /// // Values between `0` and `min` are Subnormal.
385+ /// assert!(lower_than_min.is_subnormal());
386+ /// # }
387+ /// ```
388+ ///
389+ /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
390+ #[ inline]
391+ #[ must_use]
392+ #[ cfg( not( bootstrap) ) ]
393+ #[ unstable( feature = "f128" , issue = "116909" ) ]
394+ #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
395+ pub const fn is_subnormal ( self ) -> bool {
396+ matches ! ( self . classify( ) , FpCategory :: Subnormal )
397+ }
398+
399+ /// Returns `true` if the number is neither zero, infinite, [subnormal], or NaN.
400+ ///
401+ /// ```
402+ /// #![feature(f128)]
403+ /// # // FIXME(f16_f128): remove when `eqtf2` is available
404+ /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
405+ ///
406+ /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128
407+ /// let max = f128::MAX;
408+ /// let lower_than_min = 1.0e-4960_f128;
409+ /// let zero = 0.0_f128;
410+ ///
411+ /// assert!(min.is_normal());
412+ /// assert!(max.is_normal());
413+ ///
414+ /// assert!(!zero.is_normal());
415+ /// assert!(!f128::NAN.is_normal());
416+ /// assert!(!f128::INFINITY.is_normal());
417+ /// // Values between `0` and `min` are Subnormal.
418+ /// assert!(!lower_than_min.is_normal());
419+ /// # }
420+ /// ```
421+ ///
422+ /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
423+ #[ inline]
424+ #[ must_use]
425+ #[ cfg( not( bootstrap) ) ]
426+ #[ unstable( feature = "f128" , issue = "116909" ) ]
427+ #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
428+ pub const fn is_normal ( self ) -> bool {
429+ matches ! ( self . classify( ) , FpCategory :: Normal )
430+ }
431+
432+ /// Returns the floating point category of the number. If only one property
433+ /// is going to be tested, it is generally faster to use the specific
434+ /// predicate instead.
435+ ///
436+ /// ```
437+ /// #![feature(f128)]
438+ /// # // FIXME(f16_f128): remove when `eqtf2` is available
439+ /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
440+ ///
441+ /// use std::num::FpCategory;
442+ ///
443+ /// let num = 12.4_f128;
444+ /// let inf = f128::INFINITY;
445+ ///
446+ /// assert_eq!(num.classify(), FpCategory::Normal);
447+ /// assert_eq!(inf.classify(), FpCategory::Infinite);
448+ /// # }
449+ /// ```
450+ #[ inline]
451+ #[ cfg( not( bootstrap) ) ]
452+ #[ unstable( feature = "f128" , issue = "116909" ) ]
453+ #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
454+ pub const fn classify ( self ) -> FpCategory {
455+ // Other float types cannot use a bitwise classify because they may suffer a variety
456+ // of errors if the backend chooses to cast to different float types (x87). `f128` cannot
457+ // fit into any other float types so this is not a concern, and we rely on bit patterns.
458+
459+ // SAFETY: POD bitcast, same as in `to_bits`.
460+ let bits = unsafe { mem:: transmute :: < f128 , u128 > ( self ) } ;
461+ Self :: classify_bits ( bits)
462+ }
463+
464+ /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
465+ /// FIXME(jubilee): In a just world, this would be the entire impl for classify,
466+ /// plus a transmute. We do not live in a just world, but we can make it more so.
467+ #[ inline]
468+ #[ rustc_const_unstable( feature = "const_float_classify" , issue = "72505" ) ]
469+ const fn classify_bits ( b : u128 ) -> FpCategory {
470+ match ( b & Self :: MAN_MASK , b & Self :: EXP_MASK ) {
471+ ( 0 , Self :: EXP_MASK ) => FpCategory :: Infinite ,
472+ ( _, Self :: EXP_MASK ) => FpCategory :: Nan ,
473+ ( 0 , 0 ) => FpCategory :: Zero ,
474+ ( _, 0 ) => FpCategory :: Subnormal ,
475+ _ => FpCategory :: Normal ,
476+ }
477+ }
478+
357479 /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
358480 /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
359481 /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
@@ -638,12 +760,52 @@ impl f128 {
638760 /// ```
639761 #[ inline]
640762 #[ unstable( feature = "f128" , issue = "116909" ) ]
763+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
641764 #[ must_use = "this returns the result of the operation, without modifying the original" ]
642- pub fn to_bits ( self ) -> u128 {
643- // SAFETY: `u128` is a plain old datatype so we can always... uh...
644- // ...look, just pretend you forgot what you just read.
645- // Stability concerns.
646- 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+ #[ inline]
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 available on all platforms (needs `netf2` and `unordtf2`). So classify
786+ // the bits instead.
787+
788+ // SAFETY: this is a POD transmutation
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)
647809 }
648810
649811 /// Raw transmutation from `u128`.
@@ -688,11 +850,52 @@ impl f128 {
688850 #[ inline]
689851 #[ must_use]
690852 #[ unstable( feature = "f128" , issue = "116909" ) ]
691- pub fn from_bits ( v : u128 ) -> Self {
692- // SAFETY: `u128 is a plain old datatype so we can always... uh...
693- // ...look, just pretend you forgot what you just read.
694- // Stability concerns.
695- 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+ #[ inline]
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)
696899 }
697900
698901 /// Return the memory representation of this floating point number as a byte array in
@@ -715,8 +918,9 @@ impl f128 {
715918 /// ```
716919 #[ inline]
717920 #[ unstable( feature = "f128" , issue = "116909" ) ]
921+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
718922 #[ must_use = "this returns the result of the operation, without modifying the original" ]
719- pub fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
923+ pub const fn to_be_bytes ( self ) -> [ u8 ; 16 ] {
720924 self . to_bits ( ) . to_be_bytes ( )
721925 }
722926
@@ -740,8 +944,9 @@ impl f128 {
740944 /// ```
741945 #[ inline]
742946 #[ unstable( feature = "f128" , issue = "116909" ) ]
947+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
743948 #[ must_use = "this returns the result of the operation, without modifying the original" ]
744- pub fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
949+ pub const fn to_le_bytes ( self ) -> [ u8 ; 16 ] {
745950 self . to_bits ( ) . to_le_bytes ( )
746951 }
747952
@@ -776,8 +981,9 @@ impl f128 {
776981 /// ```
777982 #[ inline]
778983 #[ unstable( feature = "f128" , issue = "116909" ) ]
984+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
779985 #[ must_use = "this returns the result of the operation, without modifying the original" ]
780- pub fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
986+ pub const fn to_ne_bytes ( self ) -> [ u8 ; 16 ] {
781987 self . to_bits ( ) . to_ne_bytes ( )
782988 }
783989
@@ -803,7 +1009,8 @@ impl f128 {
8031009 #[ inline]
8041010 #[ must_use]
8051011 #[ unstable( feature = "f128" , issue = "116909" ) ]
806- 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 {
8071014 Self :: from_bits ( u128:: from_be_bytes ( bytes) )
8081015 }
8091016
@@ -829,7 +1036,8 @@ impl f128 {
8291036 #[ inline]
8301037 #[ must_use]
8311038 #[ unstable( feature = "f128" , issue = "116909" ) ]
832- 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 {
8331041 Self :: from_bits ( u128:: from_le_bytes ( bytes) )
8341042 }
8351043
@@ -865,7 +1073,8 @@ impl f128 {
8651073 #[ inline]
8661074 #[ must_use]
8671075 #[ unstable( feature = "f128" , issue = "116909" ) ]
868- 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 {
8691078 Self :: from_bits ( u128:: from_ne_bytes ( bytes) )
8701079 }
8711080
0 commit comments