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;
18
+ use crate :: num:: FpCategory ;
16
19
17
20
/// Basic mathematical constants.
18
21
#[ unstable( feature = "f128" , issue = "116909" ) ]
@@ -251,6 +254,12 @@ impl f128 {
251
254
#[ cfg( not( bootstrap) ) ]
252
255
pub ( crate ) const SIGN_MASK : u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000 ;
253
256
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
+
254
263
/// Minimum representable positive value (min subnormal)
255
264
#[ cfg( not( bootstrap) ) ]
256
265
const TINY_BITS : u128 = 0x1 ;
@@ -354,6 +363,119 @@ impl f128 {
354
363
self . abs_private ( ) < Self :: INFINITY
355
364
}
356
365
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
+
357
479
/// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
358
480
/// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
359
481
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
@@ -638,12 +760,52 @@ impl f128 {
638
760
/// ```
639
761
#[ inline]
640
762
#[ unstable( feature = "f128" , issue = "116909" ) ]
763
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
641
764
#[ 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)
647
809
}
648
810
649
811
/// Raw transmutation from `u128`.
@@ -688,11 +850,52 @@ impl f128 {
688
850
#[ inline]
689
851
#[ must_use]
690
852
#[ 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)
696
899
}
697
900
698
901
/// Return the memory representation of this floating point number as a byte array in
@@ -715,8 +918,9 @@ impl f128 {
715
918
/// ```
716
919
#[ inline]
717
920
#[ unstable( feature = "f128" , issue = "116909" ) ]
921
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
718
922
#[ 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 ] {
720
924
self . to_bits ( ) . to_be_bytes ( )
721
925
}
722
926
@@ -740,8 +944,9 @@ impl f128 {
740
944
/// ```
741
945
#[ inline]
742
946
#[ unstable( feature = "f128" , issue = "116909" ) ]
947
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
743
948
#[ 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 ] {
745
950
self . to_bits ( ) . to_le_bytes ( )
746
951
}
747
952
@@ -776,8 +981,9 @@ impl f128 {
776
981
/// ```
777
982
#[ inline]
778
983
#[ unstable( feature = "f128" , issue = "116909" ) ]
984
+ #[ rustc_const_unstable( feature = "const_float_bits_conv" , issue = "72447" ) ]
779
985
#[ 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 ] {
781
987
self . to_bits ( ) . to_ne_bytes ( )
782
988
}
783
989
@@ -803,7 +1009,8 @@ impl f128 {
803
1009
#[ inline]
804
1010
#[ must_use]
805
1011
#[ 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 {
807
1014
Self :: from_bits ( u128:: from_be_bytes ( bytes) )
808
1015
}
809
1016
@@ -829,7 +1036,8 @@ impl f128 {
829
1036
#[ inline]
830
1037
#[ must_use]
831
1038
#[ 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 {
833
1041
Self :: from_bits ( u128:: from_le_bytes ( bytes) )
834
1042
}
835
1043
@@ -865,7 +1073,8 @@ impl f128 {
865
1073
#[ inline]
866
1074
#[ must_use]
867
1075
#[ 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 {
869
1078
Self :: from_bits ( u128:: from_ne_bytes ( bytes) )
870
1079
}
871
1080
0 commit comments