diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 7d21f9a9a66d0..39ddae9263cbf 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -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)] diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 6313de31ce4d5..cee358627be94 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -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) } @@ -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 } @@ -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 @@ -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 @@ -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; @@ -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() } @@ -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 @@ -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) } } @@ -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) } @@ -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() } @@ -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() } @@ -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() } @@ -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)) } @@ -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)) } @@ -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)) } diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index d42e5392c5863..d3af87e1e4cec 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -380,8 +380,9 @@ 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 } @@ -389,7 +390,8 @@ impl f64 { // 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) } @@ -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 } @@ -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 @@ -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 @@ -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; @@ -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() } @@ -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() } @@ -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) } } @@ -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) } @@ -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() } @@ -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() } @@ -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() } @@ -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)) } @@ -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)) } @@ -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)) } diff --git a/src/test/ui/consts/const-float-bits-conv.rs b/src/test/ui/consts/const-float-bits-conv.rs new file mode 100644 index 0000000000000..72b42a4b54c25 --- /dev/null +++ b/src/test/ui/consts/const-float-bits-conv.rs @@ -0,0 +1,86 @@ +// -Zmir-opt-level=0 +// run-pass + +#![feature(const_panic)] +#![feature(const_if_match)] +#![feature(const_float_bits_conv)] +#![feature(const_float_classify)] + +// Don't promote +const fn nop(x: T) -> T { x } + +macro_rules! const_assert { + ($a:expr) => { + { + const _: () = assert!($a); + assert!(nop($a)); + } + }; + ($a:expr, $b:expr) => { + { + const _: () = assert!($a == $b); + assert_eq!(nop($a), nop($b)); + } + }; +} + +fn f32() { + const_assert!((1f32).to_bits(), 0x3f800000); + const_assert!(u32::from_be_bytes(1f32.to_be_bytes()), 0x3f800000); + const_assert!((12.5f32).to_bits(), 0x41480000); + const_assert!(u32::from_le_bytes(12.5f32.to_le_bytes()), 0x41480000); + const_assert!((1337f32).to_bits(), 0x44a72000); + const_assert!(u32::from_ne_bytes(1337f32.to_ne_bytes()), 0x44a72000); + const_assert!((-14.25f32).to_bits(), 0xc1640000); + const_assert!(f32::from_bits(0x3f800000), 1.0); + const_assert!(f32::from_be_bytes(0x3f800000u32.to_be_bytes()), 1.0); + const_assert!(f32::from_bits(0x41480000), 12.5); + const_assert!(f32::from_le_bytes(0x41480000u32.to_le_bytes()), 12.5); + const_assert!(f32::from_bits(0x44a72000), 1337.0); + const_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0); + const_assert!(f32::from_bits(0xc1640000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signalingness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA; + const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555; + + const_assert!(f32::from_bits(MASKED_NAN1).is_nan()); + const_assert!(f32::from_bits(MASKED_NAN1).is_nan()); + + const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1); + const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2); +} + +fn f64() { + const_assert!((1f64).to_bits(), 0x3ff0000000000000); + const_assert!(u64::from_be_bytes(1f64.to_be_bytes()), 0x3ff0000000000000); + const_assert!((12.5f64).to_bits(), 0x4029000000000000); + const_assert!(u64::from_le_bytes(12.5f64.to_le_bytes()), 0x4029000000000000); + const_assert!((1337f64).to_bits(), 0x4094e40000000000); + const_assert!(u64::from_ne_bytes(1337f64.to_ne_bytes()), 0x4094e40000000000); + const_assert!((-14.25f64).to_bits(), 0xc02c800000000000); + const_assert!(f64::from_bits(0x3ff0000000000000), 1.0); + const_assert!(f64::from_be_bytes(0x3ff0000000000000u64.to_be_bytes()), 1.0); + const_assert!(f64::from_bits(0x4029000000000000), 12.5); + const_assert!(f64::from_le_bytes(0x4029000000000000u64.to_le_bytes()), 12.5); + const_assert!(f64::from_bits(0x4094e40000000000), 1337.0); + const_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0); + const_assert!(f64::from_bits(0xc02c800000000000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signalingness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; + const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + + const_assert!(f64::from_bits(MASKED_NAN1).is_nan()); + const_assert!(f64::from_bits(MASKED_NAN1).is_nan()); + + const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1); + const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2); +} + +fn main() { + f32(); + f64(); +}