Skip to content

Commit

Permalink
Rollup merge of rust-lang#72449 - ecstatic-morse:const-float-bitcast,…
Browse files Browse the repository at this point in the history
… r=RalfJung,oli-obk

Const floating point bitcasts and classification

Makes the `f32` and `f64` methods described in rust-lang#72447 and rust-lang#72505 unstably const.

r? @RalfJung
  • Loading branch information
RalfJung authored Jun 12, 2020
2 parents e91bf6c + 9dadeb3 commit 802a9ac
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 36 deletions.
2 changes: 2 additions & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
#![feature(constctlz)]
#![feature(const_panic)]
#![feature(const_fn_union)]
#![feature(const_float_bits_conv)]
#![feature(const_float_classify)]
#![feature(const_generics)]
#![feature(const_ptr_offset)]
#![feature(const_ptr_offset_from)]
Expand Down
50 changes: 33 additions & 17 deletions src/libcore/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,16 +381,18 @@ impl f32 {
/// assert!(!f.is_nan());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_nan(self) -> bool {
pub const fn is_nan(self) -> bool {
self != self
}

// FIXME(#50145): `abs` is publicly unavailable in libcore due to
// concerns about portability, so this implementation is for
// private use internally.
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
fn abs_private(self) -> f32 {
const fn abs_private(self) -> f32 {
f32::from_bits(self.to_bits() & 0x7fff_ffff)
}

Expand All @@ -410,8 +412,9 @@ impl f32 {
/// assert!(neg_inf.is_infinite());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_infinite(self) -> bool {
pub const fn is_infinite(self) -> bool {
self.abs_private() == Self::INFINITY
}

Expand All @@ -430,8 +433,9 @@ impl f32 {
/// assert!(!neg_inf.is_finite());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_finite(self) -> bool {
pub const fn is_finite(self) -> bool {
// There's no need to handle NaN separately: if self is NaN,
// the comparison is not true, exactly as desired.
self.abs_private() < Self::INFINITY
Expand All @@ -457,9 +461,10 @@ impl f32 {
/// ```
/// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_normal(self) -> bool {
self.classify() == FpCategory::Normal
pub const fn is_normal(self) -> bool {
matches!(self.classify(), FpCategory::Normal)
}

/// Returns the floating point category of the number. If only one property
Expand All @@ -476,7 +481,8 @@ impl f32 {
/// assert_eq!(inf.classify(), FpCategory::Infinite);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn classify(self) -> FpCategory {
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub const fn classify(self) -> FpCategory {
const EXP_MASK: u32 = 0x7f800000;
const MAN_MASK: u32 = 0x007fffff;

Expand All @@ -501,8 +507,9 @@ impl f32 {
/// assert!(!g.is_sign_positive());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_sign_positive(self) -> bool {
pub const fn is_sign_positive(self) -> bool {
!self.is_sign_negative()
}

Expand All @@ -517,8 +524,9 @@ impl f32 {
/// assert!(g.is_sign_negative());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_sign_negative(self) -> bool {
pub const fn is_sign_negative(self) -> bool {
// IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
// applies to zeros and NaNs as well.
self.to_bits() & 0x8000_0000 != 0
Expand Down Expand Up @@ -650,8 +658,9 @@ impl f32 {
///
/// ```
#[stable(feature = "float_bits_conv", since = "1.20.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_bits(self) -> u32 {
pub const fn to_bits(self) -> u32 {
// SAFETY: `u32` is a plain old datatype so we can always transmute to it
unsafe { mem::transmute(self) }
}
Expand Down Expand Up @@ -693,8 +702,9 @@ impl f32 {
/// assert_eq!(v, 12.5);
/// ```
#[stable(feature = "float_bits_conv", since = "1.20.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_bits(v: u32) -> Self {
pub const fn from_bits(v: u32) -> Self {
// SAFETY: `u32` is a plain old datatype so we can always transmute from it
// It turns out the safety issues with sNaN were overblown! Hooray!
unsafe { mem::transmute(v) }
Expand All @@ -710,8 +720,9 @@ impl f32 {
/// assert_eq!(bytes, [0x41, 0x48, 0x00, 0x00]);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_be_bytes(self) -> [u8; 4] {
pub const fn to_be_bytes(self) -> [u8; 4] {
self.to_bits().to_be_bytes()
}

Expand All @@ -725,8 +736,9 @@ impl f32 {
/// assert_eq!(bytes, [0x00, 0x00, 0x48, 0x41]);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_le_bytes(self) -> [u8; 4] {
pub const fn to_le_bytes(self) -> [u8; 4] {
self.to_bits().to_le_bytes()
}

Expand All @@ -753,8 +765,9 @@ impl f32 {
/// );
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_ne_bytes(self) -> [u8; 4] {
pub const fn to_ne_bytes(self) -> [u8; 4] {
self.to_bits().to_ne_bytes()
}

Expand All @@ -767,8 +780,9 @@ impl f32 {
/// assert_eq!(value, 12.5);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_be_bytes(bytes: [u8; 4]) -> Self {
pub const fn from_be_bytes(bytes: [u8; 4]) -> Self {
Self::from_bits(u32::from_be_bytes(bytes))
}

Expand All @@ -781,8 +795,9 @@ impl f32 {
/// assert_eq!(value, 12.5);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_le_bytes(bytes: [u8; 4]) -> Self {
pub const fn from_le_bytes(bytes: [u8; 4]) -> Self {
Self::from_bits(u32::from_le_bytes(bytes))
}

Expand All @@ -806,8 +821,9 @@ impl f32 {
/// assert_eq!(value, 12.5);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_ne_bytes(bytes: [u8; 4]) -> Self {
pub const fn from_ne_bytes(bytes: [u8; 4]) -> Self {
Self::from_bits(u32::from_ne_bytes(bytes))
}

Expand Down
56 changes: 37 additions & 19 deletions src/libcore/num/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,16 +380,18 @@ impl f64 {
/// assert!(!f.is_nan());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_nan(self) -> bool {
pub const fn is_nan(self) -> bool {
self != self
}

// FIXME(#50145): `abs` is publicly unavailable in libcore due to
// concerns about portability, so this implementation is for
// private use internally.
#[inline]
fn abs_private(self) -> f64 {
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
const fn abs_private(self) -> f64 {
f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff)
}

Expand All @@ -409,8 +411,9 @@ impl f64 {
/// assert!(neg_inf.is_infinite());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_infinite(self) -> bool {
pub const fn is_infinite(self) -> bool {
self.abs_private() == Self::INFINITY
}

Expand All @@ -429,8 +432,9 @@ impl f64 {
/// assert!(!neg_inf.is_finite());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_finite(self) -> bool {
pub const fn is_finite(self) -> bool {
// There's no need to handle NaN separately: if self is NaN,
// the comparison is not true, exactly as desired.
self.abs_private() < Self::INFINITY
Expand All @@ -456,9 +460,10 @@ impl f64 {
/// ```
/// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_normal(self) -> bool {
self.classify() == FpCategory::Normal
pub const fn is_normal(self) -> bool {
matches!(self.classify(), FpCategory::Normal)
}

/// Returns the floating point category of the number. If only one property
Expand All @@ -475,7 +480,8 @@ impl f64 {
/// assert_eq!(inf.classify(), FpCategory::Infinite);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn classify(self) -> FpCategory {
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub const fn classify(self) -> FpCategory {
const EXP_MASK: u64 = 0x7ff0000000000000;
const MAN_MASK: u64 = 0x000fffffffffffff;

Expand All @@ -500,16 +506,18 @@ impl f64 {
/// assert!(!g.is_sign_positive());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_sign_positive(self) -> bool {
pub const fn is_sign_positive(self) -> bool {
!self.is_sign_negative()
}

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_positive")]
#[inline]
#[doc(hidden)]
pub fn is_positive(self) -> bool {
pub const fn is_positive(self) -> bool {
self.is_sign_positive()
}

Expand All @@ -524,16 +532,18 @@ impl f64 {
/// assert!(g.is_sign_negative());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub fn is_sign_negative(self) -> bool {
pub const fn is_sign_negative(self) -> bool {
self.to_bits() & 0x8000_0000_0000_0000 != 0
}

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_negative")]
#[inline]
#[doc(hidden)]
pub fn is_negative(self) -> bool {
pub const fn is_negative(self) -> bool {
self.is_sign_negative()
}

Expand Down Expand Up @@ -664,8 +674,9 @@ impl f64 {
///
/// ```
#[stable(feature = "float_bits_conv", since = "1.20.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_bits(self) -> u64 {
pub const fn to_bits(self) -> u64 {
// SAFETY: `u64` is a plain old datatype so we can always transmute to it
unsafe { mem::transmute(self) }
}
Expand Down Expand Up @@ -707,8 +718,9 @@ impl f64 {
/// assert_eq!(v, 12.5);
/// ```
#[stable(feature = "float_bits_conv", since = "1.20.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_bits(v: u64) -> Self {
pub const fn from_bits(v: u64) -> Self {
// SAFETY: `u64` is a plain old datatype so we can always transmute from it
// It turns out the safety issues with sNaN were overblown! Hooray!
unsafe { mem::transmute(v) }
Expand All @@ -724,8 +736,9 @@ impl f64 {
/// assert_eq!(bytes, [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_be_bytes(self) -> [u8; 8] {
pub const fn to_be_bytes(self) -> [u8; 8] {
self.to_bits().to_be_bytes()
}

Expand All @@ -739,8 +752,9 @@ impl f64 {
/// assert_eq!(bytes, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40]);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_le_bytes(self) -> [u8; 8] {
pub const fn to_le_bytes(self) -> [u8; 8] {
self.to_bits().to_le_bytes()
}

Expand All @@ -767,8 +781,9 @@ impl f64 {
/// );
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn to_ne_bytes(self) -> [u8; 8] {
pub const fn to_ne_bytes(self) -> [u8; 8] {
self.to_bits().to_ne_bytes()
}

Expand All @@ -781,8 +796,9 @@ impl f64 {
/// assert_eq!(value, 12.5);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
pub const fn from_be_bytes(bytes: [u8; 8]) -> Self {
Self::from_bits(u64::from_be_bytes(bytes))
}

Expand All @@ -795,8 +811,9 @@ impl f64 {
/// assert_eq!(value, 12.5);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_le_bytes(bytes: [u8; 8]) -> Self {
pub const fn from_le_bytes(bytes: [u8; 8]) -> Self {
Self::from_bits(u64::from_le_bytes(bytes))
}

Expand All @@ -820,8 +837,9 @@ impl f64 {
/// assert_eq!(value, 12.5);
/// ```
#[stable(feature = "float_to_from_bytes", since = "1.40.0")]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub fn from_ne_bytes(bytes: [u8; 8]) -> Self {
pub const fn from_ne_bytes(bytes: [u8; 8]) -> Self {
Self::from_bits(u64::from_ne_bytes(bytes))
}

Expand Down
Loading

0 comments on commit 802a9ac

Please sign in to comment.