Skip to content

Commit

Permalink
Rollup merge of rust-lang#131304 - RalfJung:float-core, r=tgross35
Browse files Browse the repository at this point in the history
float types: move copysign, abs, signum to libcore

These operations are explicitly specified to act "bitwise", i.e. they just act on the sign bit and do not even quiet signaling NaNs. We also list them as ["non-arithmetic operations"](https://doc.rust-lang.org/nightly/std/primitive.f32.html#nan-bit-patterns), and all the other non-arithmetic operations are in libcore. There's no reason to expect them to require any sort of runtime support, and from [these experiments](rust-lang#50145 (comment)) it seems like LLVM indeed compiles them in a way that does not require any sort of runtime support.

Nominating for `@rust-lang/libs-api` since this change takes immediate effect on stable.

Part of rust-lang#50145.
  • Loading branch information
workingjubilee authored Nov 14, 2024
2 parents 6439774 + a461cf9 commit f57853b
Show file tree
Hide file tree
Showing 9 changed files with 374 additions and 414 deletions.
2 changes: 1 addition & 1 deletion 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 core/src/num/f128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,17 +285,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 @@ -345,10 +334,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 @@ -836,8 +826,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 @@ -1281,4 +1271,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 core/src/num/f16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,15 +279,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 @@ -335,10 +326,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 @@ -821,8 +813,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 @@ -1256,4 +1248,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

0 comments on commit f57853b

Please sign in to comment.