Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

float types: move copysign, abs, signum to libcore #131304

Merged
merged 5 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion library/core/src/fmt/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ macro_rules! impl_general_format {
($($t:ident)*) => {
$(impl GeneralFormat for $t {
fn already_rounded_value_should_use_exponential(&self) -> bool {
let abs = $t::abs_private(*self);
let abs = $t::abs(*self);
(abs != 0.0 && abs < 1e-4) || abs >= 1e+16
}
})*
Expand Down
114 changes: 100 additions & 14 deletions library/core/src/num/f128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,17 +284,6 @@ impl f128 {
self != self
}

// FIXME(#50145): `abs` is publicly unavailable in core due to
// concerns about portability, so this implementation is for
// private use internally.
#[inline]
pub(crate) const fn abs_private(self) -> f128 {
// SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
unsafe {
mem::transmute::<u128, f128>(mem::transmute::<f128, u128>(self) & !Self::SIGN_MASK)
}
}

/// Returns `true` if this value is positive infinity or negative infinity, and
/// `false` otherwise.
///
Expand Down Expand Up @@ -344,10 +333,11 @@ impl f128 {
#[inline]
#[must_use]
#[unstable(feature = "f128", issue = "116909")]
#[rustc_allow_const_fn_unstable(const_float_methods)] // for `abs`
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
self.abs() < Self::INFINITY
}

/// Returns `true` if the number is [subnormal].
Expand Down Expand Up @@ -835,8 +825,8 @@ impl f128 {
const HI: f128 = f128::MAX / 2.;

let (a, b) = (self, other);
let abs_a = a.abs_private();
let abs_b = b.abs_private();
let abs_a = a.abs();
let abs_b = b.abs();

if abs_a <= HI && abs_b <= HI {
// Overflow is impossible
Expand Down Expand Up @@ -1282,4 +1272,100 @@ impl f128 {
}
self
}

/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let x = 3.5_f128;
/// let y = -3.5_f128;
///
/// assert_eq!(x.abs(), x);
/// assert_eq!(y.abs(), -y);
///
/// assert!(f128::NAN.abs().is_nan());
/// # }
/// ```
#[inline]
#[unstable(feature = "f128", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn abs(self) -> Self {
// FIXME(f16_f128): replace with `intrinsics::fabsf128` when available
// We don't do this now because LLVM has lowering bugs for f128 math.
Self::from_bits(self.to_bits() & !(1 << 127))
}

/// 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
///
/// ```
/// #![feature(f128)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let f = 3.5_f128;
///
/// assert_eq!(f.signum(), 1.0);
/// assert_eq!(f128::NEG_INFINITY.signum(), -1.0);
///
/// assert!(f128::NAN.signum().is_nan());
/// # }
/// ```
#[inline]
#[unstable(feature = "f128", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn signum(self) -> f128 {
if self.is_nan() { Self::NAN } else { 1.0_f128.copysign(self) }
}

/// Returns a number composed of the magnitude of `self` and the sign of
/// `sign`.
///
/// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`.
/// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is
/// returned.
///
/// If `sign` is a NaN, then this operation will still carry over its sign into the result. 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 `copysign` with `sign` being a NaN might produce an unexpected or non-portable
/// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more
/// info.
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let f = 3.5_f128;
///
/// assert_eq!(f.copysign(0.42), 3.5_f128);
/// assert_eq!(f.copysign(-0.42), -3.5_f128);
/// assert_eq!((-f).copysign(0.42), 3.5_f128);
/// assert_eq!((-f).copysign(-0.42), -3.5_f128);
///
/// assert!(f128::NAN.copysign(1.0).is_nan());
/// # }
/// ```
#[inline]
#[unstable(feature = "f128", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn copysign(self, sign: f128) -> f128 {
// SAFETY: this is actually a safe intrinsic
unsafe { intrinsics::copysignf128(self, sign) }
}
}
111 changes: 99 additions & 12 deletions library/core/src/num/f16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,15 +278,6 @@ impl f16 {
self != self
}

// FIXMxE(#50145): `abs` is publicly unavailable in core due to
// concerns about portability, so this implementation is for
// private use internally.
#[inline]
pub(crate) const fn abs_private(self) -> f16 {
// SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
unsafe { mem::transmute::<u16, f16>(mem::transmute::<f16, u16>(self) & !Self::SIGN_MASK) }
}

/// Returns `true` if this value is positive infinity or negative infinity, and
/// `false` otherwise.
///
Expand Down Expand Up @@ -334,10 +325,11 @@ impl f16 {
#[inline]
#[must_use]
#[unstable(feature = "f16", issue = "116909")]
#[rustc_allow_const_fn_unstable(const_float_methods)] // for `abs`
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
self.abs() < Self::INFINITY
}

/// Returns `true` if the number is [subnormal].
Expand Down Expand Up @@ -820,8 +812,8 @@ impl f16 {
const HI: f16 = f16::MAX / 2.;

let (a, b) = (self, other);
let abs_a = a.abs_private();
let abs_b = b.abs_private();
let abs_a = a.abs();
let abs_b = b.abs();

if abs_a <= HI && abs_b <= HI {
// Overflow is impossible
Expand Down Expand Up @@ -1257,4 +1249,99 @@ impl f16 {
}
self
}

/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let x = 3.5_f16;
/// let y = -3.5_f16;
///
/// assert_eq!(x.abs(), x);
/// assert_eq!(y.abs(), -y);
///
/// assert!(f16::NAN.abs().is_nan());
/// # }
/// ```
#[inline]
#[unstable(feature = "f16", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn abs(self) -> Self {
// FIXME(f16_f128): replace with `intrinsics::fabsf16` when available
Self::from_bits(self.to_bits() & !(1 << 15))
}

/// 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
///
/// ```
/// #![feature(f16)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let f = 3.5_f16;
///
/// assert_eq!(f.signum(), 1.0);
/// assert_eq!(f16::NEG_INFINITY.signum(), -1.0);
///
/// assert!(f16::NAN.signum().is_nan());
/// # }
/// ```
#[inline]
#[unstable(feature = "f16", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn signum(self) -> f16 {
if self.is_nan() { Self::NAN } else { 1.0_f16.copysign(self) }
}

/// Returns a number composed of the magnitude of `self` and the sign of
/// `sign`.
///
/// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`.
/// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is
/// returned.
///
/// If `sign` is a NaN, then this operation will still carry over its sign into the result. 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 `copysign` with `sign` being a NaN might produce an unexpected or non-portable
/// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more
/// info.
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let f = 3.5_f16;
///
/// assert_eq!(f.copysign(0.42), 3.5_f16);
/// assert_eq!(f.copysign(-0.42), -3.5_f16);
/// assert_eq!((-f).copysign(0.42), 3.5_f16);
/// assert_eq!((-f).copysign(-0.42), -3.5_f16);
///
/// assert!(f16::NAN.copysign(1.0).is_nan());
/// # }
/// ```
#[inline]
#[unstable(feature = "f16", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_methods", issue = "130843")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn copysign(self, sign: f16) -> f16 {
// SAFETY: this is actually a safe intrinsic
unsafe { intrinsics::copysignf16(self, sign) }
}
}
Loading
Loading