From e8e2d614986894d0462d7f21fa26ab28e72e9b10 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 22 Sep 2026 14:24:30 +0200 Subject: [PATCH 1/5] add `ppcf128` API --- ...sroot_tests-128bit-atomic-operations.patch | 4 +- library/coretests/tests/lib.rs | 2 + library/coretests/tests/num/mod.rs | 2 + library/coretests/tests/num/ppcf128.rs | 155 ++++++++++++++++++ .../crates/core_arch/src/powerpc/mod.rs | 140 ++++++++++++++++ 5 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 library/coretests/tests/num/ppcf128.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 7194d8144ca69..b48685cc4aae9 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -14,8 +14,10 @@ diff --git a/coretests/tests/lib.rs b/coretests/tests/lib.rs index 1e336bf..35e6f54 100644 --- a/coretests/tests/lib.rs +++ b/coretests/tests/lib.rs -@@ -2,4 +2,3 @@ +@@ -2,7 +2,6 @@ // tidy-alphabetical-start + #![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(powerpc_ppcf128))] + #![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc))] #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index bd53680408882..f441a2ad7dd4d 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,4 +1,6 @@ // tidy-alphabetical-start +#![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(powerpc_ppcf128))] +#![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc))] #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 46e5ee0213b04..7e76fad9db347 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -39,6 +39,8 @@ mod midpoint; mod nan; mod niche_types; mod ops; +#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] +mod ppcf128; mod wrapping; use floats::{assert_biteq, float_test}; diff --git a/library/coretests/tests/num/ppcf128.rs b/library/coretests/tests/num/ppcf128.rs new file mode 100644 index 0000000000000..35be014b40fc0 --- /dev/null +++ b/library/coretests/tests/num/ppcf128.rs @@ -0,0 +1,155 @@ +#[cfg(target_arch = "powerpc")] +use core::arch::powerpc::ppcf128; +#[cfg(target_arch = "powerpc64")] +use core::arch::powerpc64::ppcf128; +use std::assert_matches; + +const _: () = assert!(size_of::() == 16); +const _: () = assert!(align_of::() == 16); + +#[test] +fn constants() { + assert_eq!( + ppcf128::MIN.to_le_bytes(), + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, // + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xfc, + ] + ); + + assert_eq!( + ppcf128::MAX.to_le_bytes(), + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f, // + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x7c, + ] + ); + + assert_eq!( + ppcf128::NAN.to_le_bytes(), + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ); + + assert_eq!( + ppcf128::INFINITY.to_le_bytes(), + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ); + + assert_eq!( + ppcf128::NEG_INFINITY.to_le_bytes(), + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ); +} + +#[test] +fn impls() { + const ONE: ppcf128 = ppcf128::from_components(1.0, 0.0); + const TWO: ppcf128 = ppcf128::from_components(2.0, 0.0); + + assert_eq!(ONE, ONE); + assert_ne!(ONE, TWO); + + assert!(ONE <= ONE); + assert!(ONE < TWO); + assert!(TWO > ONE); + assert!(TWO >= TWO); + + assert!(ONE < ppcf128::INFINITY); + assert!(ONE > ppcf128::NEG_INFINITY); + + assert_ne!(ONE, ppcf128::NAN); + assert_ne!(ppcf128::NAN, ppcf128::NAN); + + assert_eq!(ppcf128::default().to_components(), (0.0, 0.0)); +} + +macro_rules! assert_eq_normalized { + (($large:expr, $small:expr) ==> ($expected_large:expr, $expected_small:expr)) => {{ + let (large, small) = ppcf128::from_components($large, $small).to_components(); + let (expected_large, expected_small): (f64, f64) = ($expected_large, $expected_small); + + if expected_large.is_nan() { + assert!(large.is_nan(), "expected NaN, got {large}"); + } else { + assert_eq!(large, expected_large); + } + + if expected_small.is_nan() { + assert!(small.is_nan(), "expected NaN, got {small}"); + } else { + assert_eq!(small, expected_small); + } + }}; +} + +#[test] +fn normalize() { + // Already normalized. + assert_eq_normalized!((1.0, 0.0) ==> (1.0, 0.0)); + assert_eq_normalized!((-1.0, 0.0) ==> (-1.0, 0.0)); + + // The value is normalized. + assert_eq_normalized!((1.0, 0.5) ==> (1.5, 0.0)); + assert_eq_normalized!((-1.0, 0.5) ==> (-0.5, 0.0)); + assert_eq_normalized!((0.0, 1.0) ==> (1.0, 0.0)); + assert_eq_normalized!((0.0, -1.0) ==> (-1.0, 0.0)); + + let large = 2.0f64.powi(53); + assert_eq_normalized!((large, -(large - 1.0)) ==> (1.0, 0.0)); + + // Exact cancellation. + assert_eq_normalized!((1.0, -1.0) ==> (0.0, 0.0)); + assert_eq_normalized!((-1.0, 1.0) ==> (0.0, 0.0)); + + // A component too small to affect the high part remains in the low part. + let half_ulp = f64::EPSILON / 2.0; + assert_eq_normalized!((1.0, half_ulp) ==> (1.0, half_ulp)); + assert_eq_normalized!((-1.0, -half_ulp) ==> (-1.0, -half_ulp)); + + assert_eq_normalized!((1.0, -half_ulp) ==> (1.0 - half_ulp, 0.0)); + assert_eq_normalized!((-1.0, half_ulp) ==> (-1.0 + half_ulp, 0.0)); + + // Rounding the sum produces a compensating low component. + let three_quarters_ulp = 3.0 * f64::EPSILON / 4.0; + assert_eq_normalized!( + (1.0, three_quarters_ulp) ==> + (1.0 + f64::EPSILON, -f64::EPSILON / 4.0) + ); + + // Normalization works across a large difference in exponent. + let large = 2.0f64.powi(100); + assert_eq_normalized!((1.0, large) ==> (large, 1.0)); + + // Infinity with a zero low component is already normalized. + assert_eq_normalized!((f64::INFINITY, 0.0) ==> (f64::INFINITY, 0.0)); + assert_eq_normalized!((f64::NEG_INFINITY, 0.0) ==> (f64::NEG_INFINITY, 0.0)); + + // The small component of an infinity is normalized to zero. + assert_eq_normalized!((f64::INFINITY, 42.0) ==> (f64::INFINITY, 0.0)); + assert_eq_normalized!((f64::NEG_INFINITY, 42.0) ==> (f64::NEG_INFINITY, 0.0)); + + // A finite high component combined with infinity normalizes to infinity. + assert_eq_normalized!((1.0, f64::INFINITY) ==> (f64::INFINITY, 0.0)); + assert_eq_normalized!((1.0, f64::NEG_INFINITY) ==> (f64::NEG_INFINITY, 0.0)); + + // Opposite infinities produce NaN. + assert_eq_normalized!( + (f64::INFINITY, f64::NEG_INFINITY) ==> + (f64::NAN, 0.0) + ); + + assert_eq_normalized!((1.0, f64::NAN) ==> (f64::NAN, 0.0)); + assert_eq_normalized!((f64::NAN, 1.0) ==> (f64::NAN, 1.0)); + + assert_matches!(ppcf128::checked_from_components(1.0, 0.0), Some(_)); + assert_matches!(ppcf128::checked_from_components(0.0, 1.0), None); +} diff --git a/library/stdarch/crates/core_arch/src/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index 57fcd17852157..2c8b13bd4bb0e 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -22,7 +22,58 @@ impl crate::fmt::Debug for ppcf128 { } } +#[unstable(feature = "powerpc_ppcf128", issue = "163323")] +impl Default for ppcf128 { + fn default() -> Self { + Self::from_components(0.0, 0.0) + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "163323")] +impl PartialEq for ppcf128 { + #[inline] + fn eq(&self, other: &ppcf128) -> bool { + self.to_components() == other.to_components() + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "163323")] +impl PartialOrd for ppcf128 { + #[inline] + fn partial_cmp(&self, other: &ppcf128) -> Option { + self.to_components().partial_cmp(&other.to_components()) + } +} + impl ppcf128 { + /// The size of this float type in bits. + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + const BITS: u32 = 128; + + /// Smallest finite `ppcf128` value. + /// + /// Equal to −[`MAX`](Self::MAX). + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const MIN: Self = + unsafe { Self::from_components_unchecked(f64::MIN, f64::MIN * f64::EPSILON / 4.0) }; + + /// Largest finite `ppcf128` value. + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const MAX: Self = + unsafe { Self::from_components_unchecked(f64::MAX, f64::MAX * f64::EPSILON / 4.0) }; + + /// Not a Number (NaN). + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const NAN: Self = Self::from_components(f64::NAN, 0.0); + + /// Infinity (∞). + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const INFINITY: Self = Self::from_components(f64::INFINITY, 0.0); + + /// Negative infinity (−∞). + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const NEG_INFINITY: Self = Self::from_components(f64::NEG_INFINITY, 0.0); + /// Returns the memory representation of this floating point number as a byte array in /// native byte order. #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -32,6 +83,34 @@ impl ppcf128 { unsafe { crate::mem::transmute::(self) } } + /// Returns the memory representation of this floating point number as a byte array in + /// little-endian byte order. + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; 16] { + let mut bytes = self.to_ne_bytes(); + if cfg!(target_endian = "big") { + bytes[..8].reverse(); + bytes[8..].reverse(); + } + bytes + } + + /// Returns the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; 16] { + let mut bytes = self.to_ne_bytes(); + if cfg!(target_endian = "little") { + bytes[..8].reverse(); + bytes[8..].reverse(); + } + bytes + } + /// Returns the large and small component of this floating point number. #[unstable(feature = "powerpc_ppcf128", issue = "163323")] #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -41,6 +120,67 @@ impl ppcf128 { let [large, small] = unsafe { crate::mem::transmute::(self) }; (large, small) } + + /// Check whether the large and small component are in normal form. + const fn is_normal_form(large: f64, small: f64) -> bool { + if large.is_nan() { + true + } else if large.is_infinite() { + // NOTE: only the elfv2 spec requires this explicitly, but we suspect that + // it is required implicitly. Without it, infinity is not unique. + small == 0.0 + } else { + large + small == large + } + } + + /// Create a [`ppcf128`] from its large and small components. + /// + /// This function will normalize the components if they are not already in normal form. + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const fn from_components(x: f64, y: f64) -> Self { + let (large, small) = if Self::is_normal_form(x, y) { + (x, y) + } else if !(x + y).is_finite() { + (x + y, 0.0) + } else { + let large = x + y; + // Per https://doi.org/10.1145/3121432, Algorithm 2 + let x1 = large - y; + let y1 = large - x1; + let x2 = x - x1; + let y2 = y - y1; + let small = x2 + y2; + debug_assert!(Self::is_normal_form(large, small)); + (large, small) + }; + + // SAFETY: the components are in normal form. + unsafe { Self::from_components_unchecked(large, small) } + } + + /// Create a [`ppcf128`] from its large and small components. + /// + /// # Safety + /// + /// This function is safe to call only when the large and small components are normalized. + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const unsafe fn from_components_unchecked(large: f64, small: f64) -> Self { + unsafe { core::mem::transmute([large, small]) } + } + + /// Create a [`ppcf128`] from its large and small components. + /// + /// Returns `None` when the components are not in normal form. + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + pub const fn checked_from_components(large: f64, small: f64) -> Option { + if Self::is_normal_form(large, small) { + // SAFETY: the components are in normal form. + Some(unsafe { Self::from_components_unchecked(large, small) }) + } else { + None + } + } } mod altivec; From ee0b258b2b1e971c76e970211d5bf4031ed8b83f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 15 Oct 2026 01:09:06 +0200 Subject: [PATCH 2/5] doc updates --- library/coretests/tests/num/ppcf128.rs | 17 + .../crates/core_arch/src/powerpc/mod.rs | 390 +++++++++++++++++- 2 files changed, 396 insertions(+), 11 deletions(-) diff --git a/library/coretests/tests/num/ppcf128.rs b/library/coretests/tests/num/ppcf128.rs index 35be014b40fc0..84a8087b750f5 100644 --- a/library/coretests/tests/num/ppcf128.rs +++ b/library/coretests/tests/num/ppcf128.rs @@ -53,6 +53,7 @@ fn constants() { #[test] fn impls() { const ONE: ppcf128 = ppcf128::from_components(1.0, 0.0); + const NEGATIVE_ONE: ppcf128 = ppcf128::from_components(-1.0, 0.0); const TWO: ppcf128 = ppcf128::from_components(2.0, 0.0); assert_eq!(ONE, ONE); @@ -63,6 +64,22 @@ fn impls() { assert!(TWO > ONE); assert!(TWO >= TWO); + assert!(NEGATIVE_ONE <= NEGATIVE_ONE); + assert!(NEGATIVE_ONE < ONE); + assert!(ONE > NEGATIVE_ONE); + assert!(ONE >= ONE); + + const MAX: ppcf128 = ppcf128::MAX; + const ALMOST_MAX: ppcf128 = { + let (large, small) = MAX.to_components(); + ppcf128::from_components(large, small.next_down()) + }; + + assert!(ALMOST_MAX <= ALMOST_MAX); + assert!(ALMOST_MAX < MAX); + assert!(MAX > ALMOST_MAX); + assert!(MAX >= MAX); + assert!(ONE < ppcf128::INFINITY); assert!(ONE > ppcf128::NEG_INFINITY); diff --git a/library/stdarch/crates/core_arch/src/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index 2c8b13bd4bb0e..e75e86bfefe49 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -3,7 +3,35 @@ pub(crate) mod macros; /// The IBM extended-precision (double-double) floating-point type. -// FIXME(ppcf128) improve the docs. +/// +/// This type consists of two `f64` components, `large` and `small`, with the logical +/// value (in a sufficiently large floating point type) representing `large + small`. +/// +/// The intuition is that large `f64` values have less precision behind the decimal dot. +/// Combining a large `f64` with a small one provides meaningful extra precision, while +/// being able to use existing `f64` hardware instructions. +/// +/// # Normal form +/// +/// The large and small component must be in normal form. Intuitively, the large component must +/// indeed be large, and the small component should only add precision that does not fit in the +/// large component. +/// +/// More precisely, `large` and `small` are in normal form if: +/// +/// - `large.is_nan()` (`small` is irrelevant when `large.is_nan()`) +/// - `large.is_infinite()` and `small` equals `0.0` +/// - `large + small` equals `large` (as an `f64`) +/// +/// The [`ppcf128::from_components`] method automatically normalizes the components. +/// +/// # Endianness +/// +/// The `large` and `small` components are always stored as `[large, small]`. This can be +/// unintuitive when treating the bits as a `u128`. The sign bit of the large component +/// is in position 127 on big-endian, but in position 63 on little-endian. +/// +/// See [`ppcf128::to_bits()`] for an example. #[doc(alias = "__ibm128")] #[doc(alias = "doubledouble")] #[doc(alias = "f64f64")] @@ -45,6 +73,40 @@ impl PartialOrd for ppcf128 { } } +#[unstable(feature = "powerpc_ppcf128", issue = "163323")] +impl From for ppcf128 { + fn from(x: f64) -> Self { + // SAFETY: with 0.0 as the low component the value is always in normal form. + unsafe { ppcf128::from_components_unchecked(x, 0.0) } + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "163323")] +impl From for ppcf128 { + fn from(x: f32) -> Self { + Self::from(f64::from(x)) + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "163323")] +impl From for ppcf128 { + fn from(x: f16) -> Self { + Self::from(x as f64) + } +} + +macro_rules! ppcf128_doctest_prelude { + () => { + concat!( + "#![feature(powerpc_ppcf128)]\n", + "# #![feature(stdarch_powerpc)]\n", + "# #[cfg(any(target_arch = \"powerpc\", target_arch = \"powerpc64\"))] {\n", + "# #[cfg(target_arch = \"powerpc\")] use core::arch::powerpc::ppcf128;\n", + "# #[cfg(target_arch = \"powerpc64\")] use core::arch::powerpc64::ppcf128;\n", + ) + }; +} + impl ppcf128 { /// The size of this float type in bits. #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -52,7 +114,9 @@ impl ppcf128 { /// Smallest finite `ppcf128` value. /// - /// Equal to −[`MAX`](Self::MAX). + /// Equal to −[`MAX`]. + /// + /// [`MAX`]: ppcf128::MAX #[unstable(feature = "powerpc_ppcf128", issue = "163323")] pub const MIN: Self = unsafe { Self::from_components_unchecked(f64::MIN, f64::MIN * f64::EPSILON / 4.0) }; @@ -63,6 +127,17 @@ impl ppcf128 { unsafe { Self::from_components_unchecked(f64::MAX, f64::MAX * f64::EPSILON / 4.0) }; /// Not a Number (NaN). + /// + /// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are + /// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and + /// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern) + /// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more + /// info. + /// + /// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions + /// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is + /// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary. + /// The concrete bit pattern may change across Rust versions and target platforms. #[unstable(feature = "powerpc_ppcf128", issue = "163323")] pub const NAN: Self = Self::from_components(f64::NAN, 0.0); @@ -76,15 +151,59 @@ impl ppcf128 { /// Returns the memory representation of this floating point number as a byte array in /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: ppcf128::to_be_bytes + /// [`to_le_bytes`]: ppcf128::to_le_bytes + /// + /// # Examples + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let bytes = ppcf128::from(12.5_f64).to_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } + /// ); + /// # } + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] #[inline] pub const fn to_ne_bytes(self) -> [u8; 16] { - // SAFETY: every bit pattern of a `ppcf128` is a valid `[u8; 16]`. + // SAFETY: all bytes are initialized. unsafe { crate::mem::transmute::(self) } } /// Returns the memory representation of this floating point number as a byte array in /// little-endian byte order. + /// + /// # Examples + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let bytes = ppcf128::from(12.5_f64).to_le_bytes(); + /// assert_eq!( + /// bytes, + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// ); + /// # } + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] #[inline] @@ -99,6 +218,22 @@ impl ppcf128 { /// Returns the memory representation of this floating point number as a byte array in /// big-endian (network) byte order. + /// + /// # Examples + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let bytes = ppcf128::from(12.5_f64).to_be_bytes(); + /// assert_eq!( + /// bytes, + /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// ); + /// # } + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] #[inline] @@ -111,7 +246,46 @@ impl ppcf128 { bytes } + /// Raw transmutation to `u128`. + /// + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See [`f64::from_bits`](f64#method.from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(ppcf128::from(1.0_f64).to_bits(), 0x0000_0000_0000_0000_3ff0_0000_0000_0000); + /// assert_eq!(ppcf128::from(-1.0_f64).to_bits(), 0x0000_0000_0000_0000_bff0_0000_0000_0000); + /// } else { + /// assert_eq!(ppcf128::from(1.0_f64).to_bits(), 0x3ff0_0000_0000_0000_0000_0000_0000_0000); + /// assert_eq!(ppcf128::from(-1.0_f64).to_bits(), 0xbff0_0000_0000_0000_0000_0000_0000_0000); + /// } + /// # } + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn to_bits(self) -> u128 { + u128::from_ne_bytes(self.to_ne_bytes()) + } + /// Returns the large and small component of this floating point number. + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// assert_eq!(ppcf128::default().to_components(), (0.0, 0.0)); + /// assert_eq!(ppcf128::INFINITY.to_components(), (f64::INFINITY, 0.0)); + /// assert_eq!(ppcf128::MAX.to_components(), (f64::MAX, f64::MAX * f64::EPSILON / 4.0)); + /// # } + /// ``` #[unstable(feature = "powerpc_ppcf128", issue = "163323")] #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] #[inline] @@ -126,19 +300,37 @@ impl ppcf128 { if large.is_nan() { true } else if large.is_infinite() { - // NOTE: only the elfv2 spec requires this explicitly, but we suspect that + // NOTE: the elfv1 spec does not require this explicitly, but we suspect that // it is required implicitly. Without it, infinity is not unique. + // + // The elfv2 and 32-bit spec do explicitly require that the low component of + // infinity is zero. small == 0.0 } else { large + small == large } } - /// Create a [`ppcf128`] from its large and small components. + /// Creates a [`ppcf128`] from its large and small components. + /// + /// The result has the intuitive value of `large + small`. This function will normalize + /// the components if they are not already in [normal form](ppcf128#normal-form). + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] /// - /// This function will normalize the components if they are not already in normal form. + /// assert_eq!(ppcf128::from_components(1.0, 0.0).to_components(), (1.0, 0.0)); + /// assert_eq!(ppcf128::from_components(1.0, 1.0).to_components(), (2.0, 0.0)); + /// + /// assert_eq!( + /// ppcf128::from_components(f64::INFINITY, 1.0).to_components(), + /// (f64::INFINITY, 0.0) + /// ); + /// # } + /// ``` #[unstable(feature = "powerpc_ppcf128", issue = "163323")] - pub const fn from_components(x: f64, y: f64) -> Self { + pub const fn from_components(large: f64, small: f64) -> Self { + let (x, y) = (large, small); let (large, small) = if Self::is_normal_form(x, y) { (x, y) } else if !(x + y).is_finite() { @@ -159,21 +351,37 @@ impl ppcf128 { unsafe { Self::from_components_unchecked(large, small) } } - /// Create a [`ppcf128`] from its large and small components. + /// Creates a [`ppcf128`] from its large and small components. /// /// # Safety /// - /// This function is safe to call only when the large and small components are normalized. + /// This function is safe to call only when the large and small components are + /// in [normal form](ppcf128#normal-form). #[unstable(feature = "powerpc_ppcf128", issue = "163323")] pub const unsafe fn from_components_unchecked(large: f64, small: f64) -> Self { + crate::assert_unsafe_precondition!( + check_library_ub, + "components must be in normal form", + (large: f64 = large, small: f64 = small) => ppcf128::is_normal_form(large, small), + ); unsafe { core::mem::transmute([large, small]) } } - /// Create a [`ppcf128`] from its large and small components. + /// Creates a [`ppcf128`] from its large and small components. /// /// Returns `None` when the components are not in normal form. + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// assert!(ppcf128::from_components_checked(1.0, 0.0).is_some()); + /// assert!(ppcf128::from_components_checked(f64::NAN, 0.0).is_some()); + /// assert!(ppcf128::from_components_checked(0.0, 1.0).is_none()); + /// assert!(ppcf128::from_components_checked(0.0, f64::NAN).is_none()); + /// # } + /// ``` #[unstable(feature = "powerpc_ppcf128", issue = "163323")] - pub const fn checked_from_components(large: f64, small: f64) -> Option { + pub const fn from_components_checked(large: f64, small: f64) -> Option { if Self::is_normal_form(large, small) { // SAFETY: the components are in normal form. Some(unsafe { Self::from_components_unchecked(large, small) }) @@ -181,6 +389,166 @@ impl ppcf128 { None } } + + /// Returns `true` if this value is NaN. + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let nan = ppcf128::NAN; + /// let f = ppcf128::from(7.0_f64); + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// # } + /// ``` + #[must_use] + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn is_nan(self) -> bool { + self.to_components().0.is_nan() + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let f = ppcf128::from(7.0_f64); + /// let inf = ppcf128::INFINITY; + /// let neg_inf = ppcf128::NEG_INFINITY; + /// let nan = ppcf128::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// # } + /// ``` + #[must_use] + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn is_infinite(self) -> bool { + self.to_components().0.is_infinite() + } + + /// Returns `true` if this number is neither infinite nor NaN. + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let f = ppcf128::from(7.0_f64); + /// let inf = ppcf128::INFINITY; + /// let neg_inf = ppcf128::NEG_INFINITY; + /// let nan = ppcf128::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// # } + /// ``` + #[must_use] + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn is_finite(self) -> bool { + self.to_components().0.is_finite() + } + + /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with + /// positive sign bit and positive infinity. + /// + /// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of + /// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are + /// conserved over arithmetic operations, the result of `is_sign_positive` on + /// a NaN might produce an unexpected or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use + /// `self.signum() == ppcf128::from(1.0_f64)` if you need fully portable behavior + /// (evaluates to `false` for all NaNs). + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let f = ppcf128::from(7.0_f64); + /// let g = ppcf128::from(-7.0_f64); + /// + /// assert!(f.is_sign_positive()); + /// assert!(!g.is_sign_positive()); + /// # } + /// ``` + #[must_use] + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn is_sign_positive(self) -> bool { + self.to_components().0.is_sign_positive() + } + + /// Returns `true` if `self` has a negative sign, including `-0.0`, NaNs with + /// negative sign bit and negative infinity. + /// + /// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of + /// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are + /// conserved over arithmetic operations, the result of `is_sign_negative` on + /// a NaN might produce an unexpected or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use + /// `self.signum() == ppcf128::from(-1.0_f64)` if you need fully portable behavior + /// (evaluates to `false` for all NaNs). + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let f = ppcf128::from(7.0_f64); + /// let g = ppcf128::from(-7.0_f64); + /// + /// assert!(!f.is_sign_negative()); + /// assert!(g.is_sign_negative()); + /// # } + /// ``` + #[must_use] + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn is_sign_negative(self) -> bool { + self.to_components().0.is_sign_negative() + } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + #[doc = ppcf128_doctest_prelude!()] + /// + /// let f = ppcf128::from(3.5_f64); + /// + /// assert_eq!(f.signum(), ppcf128::from(1.0_f64)); + /// assert_eq!(ppcf128::NEG_INFINITY.signum(), ppcf128::from(-1.0_f64)); + /// + /// assert!(ppcf128::NAN.signum().is_nan()); + /// # } + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] + #[inline] + pub const fn signum(self) -> Self { + if self.is_nan() { + Self::NAN + } else { + Self::from_components(1.0_f64.copysign(self.to_components().0), 0.0) + } + } } mod altivec; From 366b6b5bdfec4fe48db4de6164f4b89d0215b727 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 15 Oct 2026 23:28:28 +0200 Subject: [PATCH 3/5] ignore doc tests on non-powerpc targets --- .../crates/core_arch/src/powerpc/mod.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index e75e86bfefe49..d77a1e6b33954 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -98,6 +98,12 @@ impl From for ppcf128 { macro_rules! ppcf128_doctest_prelude { () => { concat!( + "```", + // ignore the examples when running doc tests on non-powerpc targets. + cfg_select! { + any(target_arch = "powerpc", target_arch = "powerpc64") => "\n", + _ => "only-powerpc\n", + }, "#![feature(powerpc_ppcf128)]\n", "# #![feature(stdarch_powerpc)]\n", "# #[cfg(any(target_arch = \"powerpc\", target_arch = \"powerpc64\"))] {\n", @@ -160,7 +166,6 @@ impl ppcf128 { /// /// # Examples /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let bytes = ppcf128::from(12.5_f64).to_ne_bytes(); @@ -191,7 +196,6 @@ impl ppcf128 { /// /// # Examples /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let bytes = ppcf128::from(12.5_f64).to_le_bytes(); @@ -221,7 +225,6 @@ impl ppcf128 { /// /// # Examples /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let bytes = ppcf128::from(12.5_f64).to_be_bytes(); @@ -255,7 +258,6 @@ impl ppcf128 { /// /// # Examples /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// if cfg!(target_endian = "little") { @@ -278,7 +280,6 @@ impl ppcf128 { /// Returns the large and small component of this floating point number. /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// assert_eq!(ppcf128::default().to_components(), (0.0, 0.0)); @@ -316,7 +317,6 @@ impl ppcf128 { /// The result has the intuitive value of `large + small`. This function will normalize /// the components if they are not already in [normal form](ppcf128#normal-form). /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// assert_eq!(ppcf128::from_components(1.0, 0.0).to_components(), (1.0, 0.0)); @@ -371,7 +371,6 @@ impl ppcf128 { /// /// Returns `None` when the components are not in normal form. /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// assert!(ppcf128::from_components_checked(1.0, 0.0).is_some()); @@ -392,7 +391,6 @@ impl ppcf128 { /// Returns `true` if this value is NaN. /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let nan = ppcf128::NAN; @@ -413,7 +411,6 @@ impl ppcf128 { /// Returns `true` if this value is positive infinity or negative infinity, and /// `false` otherwise. /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -438,7 +435,6 @@ impl ppcf128 { /// Returns `true` if this number is neither infinite nor NaN. /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -472,7 +468,6 @@ impl ppcf128 { /// `self.signum() == ppcf128::from(1.0_f64)` if you need fully portable behavior /// (evaluates to `false` for all NaNs). /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -501,7 +496,6 @@ impl ppcf128 { /// `self.signum() == ppcf128::from(-1.0_f64)` if you need fully portable behavior /// (evaluates to `false` for all NaNs). /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -527,7 +521,6 @@ impl ppcf128 { /// /// # Examples /// - /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(3.5_f64); From e967f9803f8ed6352e871706a0d13c6019137499 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 15 Oct 2026 23:56:16 +0200 Subject: [PATCH 4/5] add representations table --- .../crates/core_arch/src/powerpc/mod.rs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/library/stdarch/crates/core_arch/src/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index d77a1e6b33954..c7f8bbc0d3f04 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -15,7 +15,8 @@ pub(crate) mod macros; /// /// The large and small component must be in normal form. Intuitively, the large component must /// indeed be large, and the small component should only add precision that does not fit in the -/// large component. +/// large component. Creating a value with components that are not in normal form +/// (e.g. with [`ppcf128::from_components_unchecked`]) is [undefined behavior]. /// /// More precisely, `large` and `small` are in normal form if: /// @@ -25,6 +26,21 @@ pub(crate) mod macros; /// /// The [`ppcf128::from_components`] method automatically normalizes the components. /// +/// ## Prefered and allowed representations +/// +/// The table below describes the representations of `ppcf128` values. The preferred representation +/// is the one used by associated constants (if applicable). The allowed representations are those +/// that don't cause [undefined behavior]. +/// +/// Value | Prefered Representation | Allowed Representations | Notes +/// ---|---|---|--- +/// +0.0 | (+0.0, +0.0) | (+0.0, ±0.0) | Nonzero second components are not allowed +/// -0.0 | (-0.0, -0.0) | (-0.0, ±0.0) | Nonzero second components are not allowed +/// +∞ | (+∞, +0.0) | (+∞, ±0.0) | Nonzero second components are not allowed +/// -∞ | (-∞, -0.0) | (-∞, ±0.0) | Nonzero second components are not allowed +/// NaN | ([NaN], +0.0) | (NaN, *) | Any NaN is allowed in the first component, any `f64` is allowed in the second component +/// x ∈ ℝ | (x₁, x₂) | N/A | x₁ + x₂ must round to x₁ +/// /// # Endianness /// /// The `large` and `small` components are always stored as `[large, small]`. This can be @@ -32,6 +48,9 @@ pub(crate) mod macros; /// is in position 127 on big-endian, but in position 63 on little-endian. /// /// See [`ppcf128::to_bits()`] for an example. +/// +/// [NaN]: f64#associatedconstant.NAN +/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[doc(alias = "__ibm128")] #[doc(alias = "doubledouble")] #[doc(alias = "f64f64")] From 7f937e44a579272c08fe1e623a5cc11dc0ebcd00 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 24 Oct 2026 17:47:04 +0200 Subject: [PATCH 5/5] remove to_bits and fix the doc prelude macro --- library/stdarch/crates/core_arch/src/mod.rs | 12 ++- .../crates/core_arch/src/powerpc/mod.rs | 101 +++++++----------- 2 files changed, 49 insertions(+), 64 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/mod.rs b/library/stdarch/crates/core_arch/src/mod.rs index ab787590a274f..0d624f4f5e069 100644 --- a/library/stdarch/crates/core_arch/src/mod.rs +++ b/library/stdarch/crates/core_arch/src/mod.rs @@ -255,7 +255,7 @@ pub mod arch { /// Platform-specific intrinsics for the `PowerPC` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "powerpc", doc))] + #[cfg(any(target_arch = "powerpc", all(doc, not(doctest))))] #[doc(cfg(target_arch = "powerpc"))] #[unstable(feature = "stdarch_powerpc", issue = "111145")] pub mod powerpc { @@ -265,7 +265,7 @@ pub mod arch { /// Platform-specific intrinsics for the `PowerPC64` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "powerpc64", doc))] + #[cfg(any(target_arch = "powerpc64", all(doc, not(doctest))))] #[doc(cfg(target_arch = "powerpc64"))] #[unstable(feature = "stdarch_powerpc", issue = "111145")] pub mod powerpc64 { @@ -368,11 +368,15 @@ mod wasm32; #[doc(cfg(any(target_arch = "mips", target_arch = "mips64")))] mod mips; -#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64", doc))] +#[cfg(any( + target_arch = "powerpc", + target_arch = "powerpc64", + all(doc, not(doctest)) +))] #[doc(cfg(any(target_arch = "powerpc", target_arch = "powerpc64")))] mod powerpc; -#[cfg(any(target_arch = "powerpc64", doc))] +#[cfg(any(target_arch = "powerpc64", all(doc, not(doctest))))] #[doc(cfg(target_arch = "powerpc64"))] mod powerpc64; diff --git a/library/stdarch/crates/core_arch/src/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index c7f8bbc0d3f04..f7b6fd67d3bb8 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -2,6 +2,20 @@ pub(crate) mod macros; +macro_rules! ppcf128_doctest_prelude { + () => { + concat!( + "#![feature(powerpc_ppcf128)]\n", + "# #![feature(stdarch_powerpc)]\n", + cfg_select! { + target_arch = "powerpc" => "# use core::arch::powerpc::ppcf128;\n", + target_arch = "powerpc64" => "# use core::arch::powerpc64::ppcf128;\n", + _ => "", // just for docs + }, + ) + }; +} + /// The IBM extended-precision (double-double) floating-point type. /// /// This type consists of two `f64` components, `large` and `small`, with the logical @@ -47,7 +61,21 @@ pub(crate) mod macros; /// unintuitive when treating the bits as a `u128`. The sign bit of the large component /// is in position 127 on big-endian, but in position 63 on little-endian. /// -/// See [`ppcf128::to_bits()`] for an example. +/// ``` +#[doc = ppcf128_doctest_prelude!()] +/// +/// fn to_bits(v: ppcf128) -> u128 { +/// u128::from_ne_bytes(v.to_ne_bytes()) +/// } +/// +/// if cfg!(target_endian = "little") { +/// assert_eq!(to_bits(ppcf128::from(1.0_f64)), 0x0000_0000_0000_0000_3ff0_0000_0000_0000); +/// assert_eq!(to_bits(ppcf128::from(-1.0_f64)), 0x0000_0000_0000_0000_bff0_0000_0000_0000); +/// } else { +/// assert_eq!(to_bits(ppcf128::from(1.0_f64)), 0x3ff0_0000_0000_0000_0000_0000_0000_0000); +/// assert_eq!(to_bits(ppcf128::from(-1.0_f64)), 0xbff0_0000_0000_0000_0000_0000_0000_0000); +/// } +/// ``` /// /// [NaN]: f64#associatedconstant.NAN /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html @@ -114,24 +142,6 @@ impl From for ppcf128 { } } -macro_rules! ppcf128_doctest_prelude { - () => { - concat!( - "```", - // ignore the examples when running doc tests on non-powerpc targets. - cfg_select! { - any(target_arch = "powerpc", target_arch = "powerpc64") => "\n", - _ => "only-powerpc\n", - }, - "#![feature(powerpc_ppcf128)]\n", - "# #![feature(stdarch_powerpc)]\n", - "# #[cfg(any(target_arch = \"powerpc\", target_arch = \"powerpc64\"))] {\n", - "# #[cfg(target_arch = \"powerpc\")] use core::arch::powerpc::ppcf128;\n", - "# #[cfg(target_arch = \"powerpc64\")] use core::arch::powerpc64::ppcf128;\n", - ) - }; -} - impl ppcf128 { /// The size of this float type in bits. #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -185,6 +195,7 @@ impl ppcf128 { /// /// # Examples /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let bytes = ppcf128::from(12.5_f64).to_ne_bytes(); @@ -198,7 +209,6 @@ impl ppcf128 { /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] /// } /// ); - /// # } /// ``` #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -215,6 +225,7 @@ impl ppcf128 { /// /// # Examples /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let bytes = ppcf128::from(12.5_f64).to_le_bytes(); @@ -223,7 +234,6 @@ impl ppcf128 { /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40, /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] /// ); - /// # } /// ``` #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -244,6 +254,7 @@ impl ppcf128 { /// /// # Examples /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let bytes = ppcf128::from(12.5_f64).to_be_bytes(); @@ -252,7 +263,6 @@ impl ppcf128 { /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] /// ); - /// # } /// ``` #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -268,43 +278,14 @@ impl ppcf128 { bytes } - /// Raw transmutation to `u128`. - /// - /// This is currently identical to `transmute::(self)` on all platforms. - /// - /// See [`f64::from_bits`](f64#method.from_bits) for some discussion of the - /// portability of this operation (there are almost no issues). - /// - /// # Examples - /// - #[doc = ppcf128_doctest_prelude!()] - /// - /// if cfg!(target_endian = "little") { - /// assert_eq!(ppcf128::from(1.0_f64).to_bits(), 0x0000_0000_0000_0000_3ff0_0000_0000_0000); - /// assert_eq!(ppcf128::from(-1.0_f64).to_bits(), 0x0000_0000_0000_0000_bff0_0000_0000_0000); - /// } else { - /// assert_eq!(ppcf128::from(1.0_f64).to_bits(), 0x3ff0_0000_0000_0000_0000_0000_0000_0000); - /// assert_eq!(ppcf128::from(-1.0_f64).to_bits(), 0xbff0_0000_0000_0000_0000_0000_0000_0000); - /// } - /// # } - /// ``` - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[unstable(feature = "powerpc_ppcf128", issue = "163323")] - #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] - #[inline] - pub const fn to_bits(self) -> u128 { - u128::from_ne_bytes(self.to_ne_bytes()) - } - /// Returns the large and small component of this floating point number. /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// assert_eq!(ppcf128::default().to_components(), (0.0, 0.0)); /// assert_eq!(ppcf128::INFINITY.to_components(), (f64::INFINITY, 0.0)); /// assert_eq!(ppcf128::MAX.to_components(), (f64::MAX, f64::MAX * f64::EPSILON / 4.0)); - /// # } /// ``` #[unstable(feature = "powerpc_ppcf128", issue = "163323")] #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -336,6 +317,7 @@ impl ppcf128 { /// The result has the intuitive value of `large + small`. This function will normalize /// the components if they are not already in [normal form](ppcf128#normal-form). /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// assert_eq!(ppcf128::from_components(1.0, 0.0).to_components(), (1.0, 0.0)); @@ -345,7 +327,6 @@ impl ppcf128 { /// ppcf128::from_components(f64::INFINITY, 1.0).to_components(), /// (f64::INFINITY, 0.0) /// ); - /// # } /// ``` #[unstable(feature = "powerpc_ppcf128", issue = "163323")] pub const fn from_components(large: f64, small: f64) -> Self { @@ -390,13 +371,13 @@ impl ppcf128 { /// /// Returns `None` when the components are not in normal form. /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// assert!(ppcf128::from_components_checked(1.0, 0.0).is_some()); /// assert!(ppcf128::from_components_checked(f64::NAN, 0.0).is_some()); /// assert!(ppcf128::from_components_checked(0.0, 1.0).is_none()); /// assert!(ppcf128::from_components_checked(0.0, f64::NAN).is_none()); - /// # } /// ``` #[unstable(feature = "powerpc_ppcf128", issue = "163323")] pub const fn from_components_checked(large: f64, small: f64) -> Option { @@ -410,6 +391,7 @@ impl ppcf128 { /// Returns `true` if this value is NaN. /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let nan = ppcf128::NAN; @@ -417,7 +399,6 @@ impl ppcf128 { /// /// assert!(nan.is_nan()); /// assert!(!f.is_nan()); - /// # } /// ``` #[must_use] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -430,6 +411,7 @@ impl ppcf128 { /// Returns `true` if this value is positive infinity or negative infinity, and /// `false` otherwise. /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -442,7 +424,6 @@ impl ppcf128 { /// /// assert!(inf.is_infinite()); /// assert!(neg_inf.is_infinite()); - /// # } /// ``` #[must_use] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -454,6 +435,7 @@ impl ppcf128 { /// Returns `true` if this number is neither infinite nor NaN. /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -466,7 +448,6 @@ impl ppcf128 { /// assert!(!nan.is_finite()); /// assert!(!inf.is_finite()); /// assert!(!neg_inf.is_finite()); - /// # } /// ``` #[must_use] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -487,6 +468,7 @@ impl ppcf128 { /// `self.signum() == ppcf128::from(1.0_f64)` if you need fully portable behavior /// (evaluates to `false` for all NaNs). /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -494,7 +476,6 @@ impl ppcf128 { /// /// assert!(f.is_sign_positive()); /// assert!(!g.is_sign_positive()); - /// # } /// ``` #[must_use] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -515,6 +496,7 @@ impl ppcf128 { /// `self.signum() == ppcf128::from(-1.0_f64)` if you need fully portable behavior /// (evaluates to `false` for all NaNs). /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(7.0_f64); @@ -522,7 +504,6 @@ impl ppcf128 { /// /// assert!(!f.is_sign_negative()); /// assert!(g.is_sign_negative()); - /// # } /// ``` #[must_use] #[unstable(feature = "powerpc_ppcf128", issue = "163323")] @@ -540,6 +521,7 @@ impl ppcf128 { /// /// # Examples /// + /// ``` #[doc = ppcf128_doctest_prelude!()] /// /// let f = ppcf128::from(3.5_f64); @@ -548,7 +530,6 @@ impl ppcf128 { /// assert_eq!(ppcf128::NEG_INFINITY.signum(), ppcf128::from(-1.0_f64)); /// /// assert!(ppcf128::NAN.signum().is_nan()); - /// # } /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "powerpc_ppcf128", issue = "163323")]