Add more `f16` and `f128` library functions and constants

This adds everything that was directly or transitively blocked on const
arithmetic for these types, which was recently merged.

Since const arithmetic is recent, most of these need to be gated by
`bootstrap`.

Anything that relies on intrinsics that are still missing is excluded.
This commit is contained in:
Trevor Gross 2024-06-18 18:25:07 -05:00
parent 0eee0557d0
commit 6e2d934a88
4 changed files with 1309 additions and 2 deletions

View File

@ -11,6 +11,7 @@
#![unstable(feature = "f128", issue = "116909")]
use crate::convert::FloatToInt;
use crate::mem;
/// Basic mathematical constants.
@ -220,12 +221,50 @@ impl f128 {
#[unstable(feature = "f128", issue = "116909")]
pub const MAX_10_EXP: i32 = 4_932;
/// 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).
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
/// and the stability of its representation over Rust versions
/// and target platforms isn't guaranteed.
#[cfg(not(bootstrap))]
#[allow(clippy::eq_op)]
#[rustc_diagnostic_item = "f128_nan"]
#[unstable(feature = "f128", issue = "116909")]
pub const NAN: f128 = 0.0_f128 / 0.0_f128;
/// Infinity (∞).
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
pub const INFINITY: f128 = 1.0_f128 / 0.0_f128;
/// Negative infinity (−∞).
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128;
/// Sign bit
#[cfg(not(bootstrap))]
pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
/// Minimum representable positive value (min subnormal)
#[cfg(not(bootstrap))]
const TINY_BITS: u128 = 0x1;
/// Minimum representable negative value (min negative subnormal)
#[cfg(not(bootstrap))]
const NEG_TINY_BITS: u128 = Self::TINY_BITS | Self::SIGN_MASK;
/// Returns `true` if this value is NaN.
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `unordtf2` is available
/// # #[cfg(target_arch = "x86_64", target_os = "linux")] {
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let nan = f128::NAN;
/// let f = 7.0_f128;
@ -243,6 +282,78 @@ 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]
#[cfg(not(bootstrap))]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub(crate) const fn abs_private(self) -> f128 {
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
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.
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let f = 7.0f128;
/// let inf = f128::INFINITY;
/// let neg_inf = f128::NEG_INFINITY;
/// let nan = f128::NAN;
///
/// assert!(!f.is_infinite());
/// assert!(!nan.is_infinite());
///
/// assert!(inf.is_infinite());
/// assert!(neg_inf.is_infinite());
/// # }
/// ```
#[inline]
#[must_use]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub const fn is_infinite(self) -> bool {
(self == f128::INFINITY) | (self == f128::NEG_INFINITY)
}
/// Returns `true` if this number is neither infinite nor NaN.
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `lttf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let f = 7.0f128;
/// let inf: f128 = f128::INFINITY;
/// let neg_inf: f128 = f128::NEG_INFINITY;
/// let nan: f128 = f128::NAN;
///
/// assert!(f.is_finite());
///
/// assert!(!nan.is_finite());
/// assert!(!inf.is_finite());
/// assert!(!neg_inf.is_finite());
/// # }
/// ```
#[inline]
#[must_use]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
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
}
/// 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
@ -292,6 +403,222 @@ impl f128 {
(self.to_bits() & (1 << 127)) != 0
}
/// Returns the least number greater than `self`.
///
/// Let `TINY` be the smallest representable positive `f128`. Then,
/// - if `self.is_nan()`, this returns `self`;
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
/// - if `self` is `-TINY`, this returns -0.0;
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
/// - otherwise the unique least value greater than `self` is returned.
///
/// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
/// is finite `x == x.next_up().next_down()` also holds.
///
/// ```rust
/// #![feature(f128)]
/// #![feature(float_next_up_down)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// // f128::EPSILON is the difference between 1.0 and the next number up.
/// assert_eq!(1.0f128.next_up(), 1.0 + f128::EPSILON);
/// // But not for most numbers.
/// assert!(0.1f128.next_up() < 0.1 + f128::EPSILON);
/// assert_eq!(4611686018427387904f128.next_up(), 4611686018427387904.000000000000001);
/// # }
/// ```
///
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
/// [`INFINITY`]: Self::INFINITY
/// [`MIN`]: Self::MIN
/// [`MAX`]: Self::MAX
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
// #[unstable(feature = "float_next_up_down", issue = "91399")]
pub fn next_up(self) -> Self {
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
// denormals to zero. This is in general unsound and unsupported, but here
// we do our best to still produce the correct result on such targets.
let bits = self.to_bits();
if self.is_nan() || bits == Self::INFINITY.to_bits() {
return self;
}
let abs = bits & !Self::SIGN_MASK;
let next_bits = if abs == 0 {
Self::TINY_BITS
} else if bits == abs {
bits + 1
} else {
bits - 1
};
Self::from_bits(next_bits)
}
/// Returns the greatest number less than `self`.
///
/// Let `TINY` be the smallest representable positive `f128`. Then,
/// - if `self.is_nan()`, this returns `self`;
/// - if `self` is [`INFINITY`], this returns [`MAX`];
/// - if `self` is `TINY`, this returns 0.0;
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
/// - otherwise the unique greatest value less than `self` is returned.
///
/// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
/// is finite `x == x.next_down().next_up()` also holds.
///
/// ```rust
/// #![feature(f128)]
/// #![feature(float_next_up_down)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let x = 1.0f128;
/// // Clamp value into range [0, 1).
/// let clamped = x.clamp(0.0, 1.0f128.next_down());
/// assert!(clamped < 1.0);
/// assert_eq!(clamped.next_up(), 1.0);
/// # }
/// ```
///
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
/// [`INFINITY`]: Self::INFINITY
/// [`MIN`]: Self::MIN
/// [`MAX`]: Self::MAX
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
// #[unstable(feature = "float_next_up_down", issue = "91399")]
pub fn next_down(self) -> Self {
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
// denormals to zero. This is in general unsound and unsupported, but here
// we do our best to still produce the correct result on such targets.
let bits = self.to_bits();
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
return self;
}
let abs = bits & !Self::SIGN_MASK;
let next_bits = if abs == 0 {
Self::NEG_TINY_BITS
} else if bits == abs {
bits - 1
} else {
bits + 1
};
Self::from_bits(next_bits)
}
/// Takes the reciprocal (inverse) of a number, `1/x`.
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let x = 2.0_f128;
/// let abs_difference = (x.recip() - (1.0 / x)).abs();
///
/// assert!(abs_difference <= f128::EPSILON);
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn recip(self) -> Self {
1.0 / self
}
/// Converts radians to degrees.
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let angle = std::f128::consts::PI;
///
/// let abs_difference = (angle.to_degrees() - 180.0).abs();
/// assert!(abs_difference <= f128::EPSILON);
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_degrees(self) -> Self {
// Use a literal for better precision.
const PIS_IN_180: f128 = 57.2957795130823208767981548141051703324054724665643215491602_f128;
self * PIS_IN_180
}
/// Converts degrees to radians.
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let angle = 180.0f128;
///
/// let abs_difference = (angle.to_radians() - std::f128::consts::PI).abs();
///
/// assert!(abs_difference <= 1e-30);
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_radians(self) -> f128 {
// Use a literal for better precision.
const RADS_PER_DEG: f128 =
0.0174532925199432957692369076848861271344287188854172545609719_f128;
self * RADS_PER_DEG
}
/// Rounds toward zero and converts to any primitive integer type,
/// assuming that the value is finite and fits in that type.
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `float*itf` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let value = 4.6_f128;
/// let rounded = unsafe { value.to_int_unchecked::<u16>() };
/// assert_eq!(rounded, 4);
///
/// let value = -128.9_f128;
/// let rounded = unsafe { value.to_int_unchecked::<i8>() };
/// assert_eq!(rounded, i8::MIN);
/// # }
/// ```
///
/// # Safety
///
/// The value must:
///
/// * Not be `NaN`
/// * Not be infinite
/// * Be representable in the return type `Int`, after truncating off its fractional part
#[inline]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub unsafe fn to_int_unchecked<Int>(self) -> Int
where
Self: FloatToInt<Int>,
{
// SAFETY: the caller must uphold the safety contract for
// `FloatToInt::to_int_unchecked`.
unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
}
/// Raw transmutation to `u128`.
///
/// This is currently identical to `transmute::<f128, u128>(self)` on all platforms.
@ -367,4 +694,315 @@ impl f128 {
// Stability concerns.
unsafe { mem::transmute(v) }
}
/// Return the memory representation of this floating point number as a byte array in
/// big-endian (network) byte order.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f128)]
///
/// let bytes = 12.5f128.to_be_bytes();
/// assert_eq!(
/// bytes,
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
/// );
/// ```
#[inline]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_be_bytes(self) -> [u8; 16] {
self.to_bits().to_be_bytes()
}
/// Return the memory representation of this floating point number as a byte array in
/// little-endian byte order.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f128)]
///
/// let bytes = 12.5f128.to_le_bytes();
/// assert_eq!(
/// bytes,
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
/// );
/// ```
#[inline]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_le_bytes(self) -> [u8; 16] {
self.to_bits().to_le_bytes()
}
/// Return 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`]: f128::to_be_bytes
/// [`to_le_bytes`]: f128::to_le_bytes
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f128)]
///
/// let bytes = 12.5f128.to_ne_bytes();
/// assert_eq!(
/// bytes,
/// if cfg!(target_endian = "big") {
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
/// } else {
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
/// }
/// );
/// ```
#[inline]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_ne_bytes(self) -> [u8; 16] {
self.to_bits().to_ne_bytes()
}
/// Create a floating point value from its representation as a byte array in big endian.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let value = f128::from_be_bytes(
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
/// );
/// assert_eq!(value, 12.5);
/// # }
/// ```
#[inline]
#[must_use]
#[unstable(feature = "f128", issue = "116909")]
pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
Self::from_bits(u128::from_be_bytes(bytes))
}
/// Create a floating point value from its representation as a byte array in little endian.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let value = f128::from_le_bytes(
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
/// );
/// assert_eq!(value, 12.5);
/// # }
/// ```
#[inline]
#[must_use]
#[unstable(feature = "f128", issue = "116909")]
pub fn from_le_bytes(bytes: [u8; 16]) -> Self {
Self::from_bits(u128::from_le_bytes(bytes))
}
/// Create a floating point value from its representation as a byte array in native endian.
///
/// As the target platform's native endianness is used, portable code
/// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
/// appropriate instead.
///
/// [`from_be_bytes`]: f128::from_be_bytes
/// [`from_le_bytes`]: f128::from_le_bytes
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `eqtf2` is available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let value = f128::from_ne_bytes(if cfg!(target_endian = "big") {
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
/// } else {
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
/// });
/// assert_eq!(value, 12.5);
/// # }
/// ```
#[inline]
#[must_use]
#[unstable(feature = "f128", issue = "116909")]
pub fn from_ne_bytes(bytes: [u8; 16]) -> Self {
Self::from_bits(u128::from_ne_bytes(bytes))
}
/// Return the ordering between `self` and `other`.
///
/// Unlike the standard partial comparison between floating point numbers,
/// this comparison always produces an ordering in accordance to
/// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision)
/// floating point standard. The values are ordered in the following sequence:
///
/// - negative quiet NaN
/// - negative signaling NaN
/// - negative infinity
/// - negative numbers
/// - negative subnormal numbers
/// - negative zero
/// - positive zero
/// - positive subnormal numbers
/// - positive numbers
/// - positive infinity
/// - positive signaling NaN
/// - positive quiet NaN.
///
/// The ordering established by this function does not always agree with the
/// [`PartialOrd`] and [`PartialEq`] implementations of `f128`. For example,
/// they consider negative and positive zero equal, while `total_cmp`
/// doesn't.
///
/// The interpretation of the signaling NaN bit follows the definition in
/// the IEEE 754 standard, which may not match the interpretation by some of
/// the older, non-conformant (e.g. MIPS) hardware implementations.
///
/// # Example
///
/// ```
/// #![feature(f128)]
///
/// struct GoodBoy {
/// name: &'static str,
/// weight: f128,
/// }
///
/// let mut bois = vec![
/// GoodBoy { name: "Pucci", weight: 0.1 },
/// GoodBoy { name: "Woofer", weight: 99.0 },
/// GoodBoy { name: "Yapper", weight: 10.0 },
/// GoodBoy { name: "Chonk", weight: f128::INFINITY },
/// GoodBoy { name: "Abs. Unit", weight: f128::NAN },
/// GoodBoy { name: "Floaty", weight: -5.0 },
/// ];
///
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
///
/// // `f128::NAN` could be positive or negative, which will affect the sort order.
/// if f128::NAN.is_sign_negative() {
/// bois.into_iter().map(|b| b.weight)
/// .zip([f128::NAN, -5.0, 0.1, 10.0, 99.0, f128::INFINITY].iter())
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
/// } else {
/// bois.into_iter().map(|b| b.weight)
/// .zip([-5.0, 0.1, 10.0, 99.0, f128::INFINITY, f128::NAN].iter())
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
/// }
/// ```
#[inline]
#[must_use]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
let mut left = self.to_bits() as i128;
let mut right = other.to_bits() as i128;
// In case of negatives, flip all the bits except the sign
// to achieve a similar layout as two's complement integers
//
// Why does this work? IEEE 754 floats consist of three fields:
// Sign bit, exponent and mantissa. The set of exponent and mantissa
// fields as a whole have the property that their bitwise order is
// equal to the numeric magnitude where the magnitude is defined.
// The magnitude is not normally defined on NaN values, but
// IEEE 754 totalOrder defines the NaN values also to follow the
// bitwise order. This leads to order explained in the doc comment.
// However, the representation of magnitude is the same for negative
// and positive numbers only the sign bit is different.
// To easily compare the floats as signed integers, we need to
// flip the exponent and mantissa bits in case of negative numbers.
// We effectively convert the numbers to "two's complement" form.
//
// To do the flipping, we construct a mask and XOR against it.
// We branchlessly calculate an "all-ones except for the sign bit"
// mask from negative-signed values: right shifting sign-extends
// the integer, so we "fill" the mask with sign bits, and then
// convert to unsigned to push one more zero bit.
// On positive values, the mask is all zeros, so it's a no-op.
left ^= (((left >> 127) as u128) >> 1) as i128;
right ^= (((right >> 127) as u128) >> 1) as i128;
left.cmp(&right)
}
/// Restrict a value to a certain interval unless it is NaN.
///
/// Returns `max` if `self` is greater than `max`, and `min` if `self` is
/// less than `min`. Otherwise this returns `self`.
///
/// Note that this function returns NaN if the initial value was NaN as
/// well.
///
/// # Panics
///
/// Panics if `min > max`, `min` is NaN, or `max` is NaN.
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// # // FIXME(f16_f128): remove when `{eq,gt,unord}tf` are available
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// assert!((-3.0f128).clamp(-2.0, 1.0) == -2.0);
/// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0);
/// assert!((2.0f128).clamp(-2.0, 1.0) == 1.0);
/// assert!((f128::NAN).clamp(-2.0, 1.0).is_nan());
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn clamp(mut self, min: f128, max: f128) -> f128 {
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
if self < min {
self = min;
}
if self > max {
self = max;
}
self
}
}

View File

@ -11,6 +11,7 @@
#![unstable(feature = "f16", issue = "116909")]
use crate::convert::FloatToInt;
use crate::mem;
/// Basic mathematical constants.
@ -215,11 +216,49 @@ impl f16 {
#[unstable(feature = "f16", issue = "116909")]
pub const MAX_10_EXP: i32 = 4;
/// 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).
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
/// and the stability of its representation over Rust versions
/// and target platforms isn't guaranteed.
#[cfg(not(bootstrap))]
#[allow(clippy::eq_op)]
#[rustc_diagnostic_item = "f16_nan"]
#[unstable(feature = "f16", issue = "116909")]
pub const NAN: f16 = 0.0_f16 / 0.0_f16;
/// Infinity (∞).
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
pub const INFINITY: f16 = 1.0_f16 / 0.0_f16;
/// Negative infinity (−∞).
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16;
/// Sign bit
#[cfg(not(bootstrap))]
const SIGN_MASK: u16 = 0x8000;
/// Minimum representable positive value (min subnormal)
#[cfg(not(bootstrap))]
const TINY_BITS: u16 = 0x1;
/// Minimum representable negative value (min negative subnormal)
#[cfg(not(bootstrap))]
const NEG_TINY_BITS: u16 = Self::TINY_BITS | Self::SIGN_MASK;
/// Returns `true` if this value is NaN.
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when ABI bugs are fixed
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// let nan = f16::NAN;
/// let f = 7.0_f16;
@ -237,6 +276,74 @@ 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]
#[cfg(not(bootstrap))]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub(crate) const fn abs_private(self) -> f16 {
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
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.
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// let f = 7.0f16;
/// let inf = f16::INFINITY;
/// let neg_inf = f16::NEG_INFINITY;
/// let nan = f16::NAN;
///
/// assert!(!f.is_infinite());
/// assert!(!nan.is_infinite());
///
/// assert!(inf.is_infinite());
/// assert!(neg_inf.is_infinite());
/// # }
/// ```
#[inline]
#[must_use]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub const fn is_infinite(self) -> bool {
(self == f16::INFINITY) | (self == f16::NEG_INFINITY)
}
/// Returns `true` if this number is neither infinite nor NaN.
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// let f = 7.0f16;
/// let inf: f16 = f16::INFINITY;
/// let neg_inf: f16 = f16::NEG_INFINITY;
/// let nan: f16 = f16::NAN;
///
/// assert!(f.is_finite());
///
/// assert!(!nan.is_finite());
/// assert!(!inf.is_finite());
/// assert!(!neg_inf.is_finite());
/// # }
/// ```
#[inline]
#[must_use]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
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
}
/// 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
@ -286,6 +393,220 @@ impl f16 {
(self.to_bits() & (1 << 15)) != 0
}
/// Returns the least number greater than `self`.
///
/// Let `TINY` be the smallest representable positive `f16`. Then,
/// - if `self.is_nan()`, this returns `self`;
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
/// - if `self` is `-TINY`, this returns -0.0;
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
/// - otherwise the unique least value greater than `self` is returned.
///
/// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
/// is finite `x == x.next_up().next_down()` also holds.
///
/// ```rust
/// #![feature(f16)]
/// #![feature(float_next_up_down)]
/// # // FIXME(f16_f128): ABI issues on MSVC
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// // f16::EPSILON is the difference between 1.0 and the next number up.
/// assert_eq!(1.0f16.next_up(), 1.0 + f16::EPSILON);
/// // But not for most numbers.
/// assert!(0.1f16.next_up() < 0.1 + f16::EPSILON);
/// assert_eq!(4356f16.next_up(), 4360.0);
/// # }
/// ```
///
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
/// [`INFINITY`]: Self::INFINITY
/// [`MIN`]: Self::MIN
/// [`MAX`]: Self::MAX
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
// #[unstable(feature = "float_next_up_down", issue = "91399")]
pub fn next_up(self) -> Self {
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
// denormals to zero. This is in general unsound and unsupported, but here
// we do our best to still produce the correct result on such targets.
let bits = self.to_bits();
if self.is_nan() || bits == Self::INFINITY.to_bits() {
return self;
}
let abs = bits & !Self::SIGN_MASK;
let next_bits = if abs == 0 {
Self::TINY_BITS
} else if bits == abs {
bits + 1
} else {
bits - 1
};
Self::from_bits(next_bits)
}
/// Returns the greatest number less than `self`.
///
/// Let `TINY` be the smallest representable positive `f16`. Then,
/// - if `self.is_nan()`, this returns `self`;
/// - if `self` is [`INFINITY`], this returns [`MAX`];
/// - if `self` is `TINY`, this returns 0.0;
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
/// - otherwise the unique greatest value less than `self` is returned.
///
/// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
/// is finite `x == x.next_down().next_up()` also holds.
///
/// ```rust
/// #![feature(f16)]
/// #![feature(float_next_up_down)]
/// # // FIXME(f16_f128): ABI issues on MSVC
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
///
/// let x = 1.0f16;
/// // Clamp value into range [0, 1).
/// let clamped = x.clamp(0.0, 1.0f16.next_down());
/// assert!(clamped < 1.0);
/// assert_eq!(clamped.next_up(), 1.0);
/// # }
/// ```
///
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
/// [`INFINITY`]: Self::INFINITY
/// [`MIN`]: Self::MIN
/// [`MAX`]: Self::MAX
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
// #[unstable(feature = "float_next_up_down", issue = "91399")]
pub fn next_down(self) -> Self {
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
// denormals to zero. This is in general unsound and unsupported, but here
// we do our best to still produce the correct result on such targets.
let bits = self.to_bits();
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
return self;
}
let abs = bits & !Self::SIGN_MASK;
let next_bits = if abs == 0 {
Self::NEG_TINY_BITS
} else if bits == abs {
bits - 1
} else {
bits + 1
};
Self::from_bits(next_bits)
}
/// Takes the reciprocal (inverse) of a number, `1/x`.
///
/// ```
/// #![feature(f16)]
/// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
/// # #[cfg(target_os = "linux")] {
///
/// let x = 2.0_f16;
/// let abs_difference = (x.recip() - (1.0 / x)).abs();
///
/// assert!(abs_difference <= f16::EPSILON);
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn recip(self) -> Self {
1.0 / self
}
/// Converts radians to degrees.
///
/// ```
/// #![feature(f16)]
/// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
/// # #[cfg(target_os = "linux")] {
///
/// let angle = std::f16::consts::PI;
///
/// let abs_difference = (angle.to_degrees() - 180.0).abs();
/// assert!(abs_difference <= 0.5);
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_degrees(self) -> Self {
// Use a literal for better precision.
const PIS_IN_180: f16 = 57.2957795130823208767981548141051703_f16;
self * PIS_IN_180
}
/// Converts degrees to radians.
///
/// ```
/// #![feature(f16)]
/// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
/// # #[cfg(target_os = "linux")] {
///
/// let angle = 180.0f16;
///
/// let abs_difference = (angle.to_radians() - std::f16::consts::PI).abs();
///
/// assert!(abs_difference <= 0.01);
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_radians(self) -> f16 {
// Use a literal for better precision.
const RADS_PER_DEG: f16 = 0.017453292519943295769236907684886_f16;
self * RADS_PER_DEG
}
/// Rounds toward zero and converts to any primitive integer type,
/// assuming that the value is finite and fits in that type.
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// let value = 4.6_f16;
/// let rounded = unsafe { value.to_int_unchecked::<u16>() };
/// assert_eq!(rounded, 4);
///
/// let value = -128.9_f16;
/// let rounded = unsafe { value.to_int_unchecked::<i8>() };
/// assert_eq!(rounded, i8::MIN);
/// # }
/// ```
///
/// # Safety
///
/// The value must:
///
/// * Not be `NaN`
/// * Not be infinite
/// * Be representable in the return type `Int`, after truncating off its fractional part
#[inline]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub unsafe fn to_int_unchecked<Int>(self) -> Int
where
Self: FloatToInt<Int>,
{
// SAFETY: the caller must uphold the safety contract for
// `FloatToInt::to_int_unchecked`.
unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
}
/// Raw transmutation to `u16`.
///
/// This is currently identical to `transmute::<f16, u16>(self)` on all platforms.
@ -362,4 +683,293 @@ impl f16 {
// Stability concerns.
unsafe { mem::transmute(v) }
}
/// Return the memory representation of this floating point number as a byte array in
/// big-endian (network) byte order.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f16)]
///
/// let bytes = 12.5f16.to_be_bytes();
/// assert_eq!(bytes, [0x4a, 0x40]);
/// ```
#[inline]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_be_bytes(self) -> [u8; 2] {
self.to_bits().to_be_bytes()
}
/// Return the memory representation of this floating point number as a byte array in
/// little-endian byte order.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f16)]
///
/// let bytes = 12.5f16.to_le_bytes();
/// assert_eq!(bytes, [0x40, 0x4a]);
/// ```
#[inline]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_le_bytes(self) -> [u8; 2] {
self.to_bits().to_le_bytes()
}
/// Return 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`]: f16::to_be_bytes
/// [`to_le_bytes`]: f16::to_le_bytes
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f16)]
///
/// let bytes = 12.5f16.to_ne_bytes();
/// assert_eq!(
/// bytes,
/// if cfg!(target_endian = "big") {
/// [0x4a, 0x40]
/// } else {
/// [0x40, 0x4a]
/// }
/// );
/// ```
#[inline]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn to_ne_bytes(self) -> [u8; 2] {
self.to_bits().to_ne_bytes()
}
/// Create a floating point value from its representation as a byte array in big endian.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// let value = f16::from_be_bytes([0x4a, 0x40]);
/// assert_eq!(value, 12.5);
/// # }
/// ```
#[inline]
#[must_use]
#[unstable(feature = "f16", issue = "116909")]
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
Self::from_bits(u16::from_be_bytes(bytes))
}
/// Create a floating point value from its representation as a byte array in little endian.
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// let value = f16::from_le_bytes([0x40, 0x4a]);
/// assert_eq!(value, 12.5);
/// # }
/// ```
#[inline]
#[must_use]
#[unstable(feature = "f16", issue = "116909")]
pub fn from_le_bytes(bytes: [u8; 2]) -> Self {
Self::from_bits(u16::from_le_bytes(bytes))
}
/// Create a floating point value from its representation as a byte array in native endian.
///
/// As the target platform's native endianness is used, portable code
/// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
/// appropriate instead.
///
/// [`from_be_bytes`]: f16::from_be_bytes
/// [`from_le_bytes`]: f16::from_le_bytes
///
/// See [`from_bits`](Self::from_bits) for some discussion of the
/// portability of this operation (there are almost no issues).
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// let value = f16::from_ne_bytes(if cfg!(target_endian = "big") {
/// [0x4a, 0x40]
/// } else {
/// [0x40, 0x4a]
/// });
/// assert_eq!(value, 12.5);
/// # }
/// ```
#[inline]
#[must_use]
#[unstable(feature = "f16", issue = "116909")]
pub fn from_ne_bytes(bytes: [u8; 2]) -> Self {
Self::from_bits(u16::from_ne_bytes(bytes))
}
/// Return the ordering between `self` and `other`.
///
/// Unlike the standard partial comparison between floating point numbers,
/// this comparison always produces an ordering in accordance to
/// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision)
/// floating point standard. The values are ordered in the following sequence:
///
/// - negative quiet NaN
/// - negative signaling NaN
/// - negative infinity
/// - negative numbers
/// - negative subnormal numbers
/// - negative zero
/// - positive zero
/// - positive subnormal numbers
/// - positive numbers
/// - positive infinity
/// - positive signaling NaN
/// - positive quiet NaN.
///
/// The ordering established by this function does not always agree with the
/// [`PartialOrd`] and [`PartialEq`] implementations of `f16`. For example,
/// they consider negative and positive zero equal, while `total_cmp`
/// doesn't.
///
/// The interpretation of the signaling NaN bit follows the definition in
/// the IEEE 754 standard, which may not match the interpretation by some of
/// the older, non-conformant (e.g. MIPS) hardware implementations.
///
/// # Example
///
/// ```
/// #![feature(f16)]
///
/// struct GoodBoy {
/// name: &'static str,
/// weight: f16,
/// }
///
/// let mut bois = vec![
/// GoodBoy { name: "Pucci", weight: 0.1 },
/// GoodBoy { name: "Woofer", weight: 99.0 },
/// GoodBoy { name: "Yapper", weight: 10.0 },
/// GoodBoy { name: "Chonk", weight: f16::INFINITY },
/// GoodBoy { name: "Abs. Unit", weight: f16::NAN },
/// GoodBoy { name: "Floaty", weight: -5.0 },
/// ];
///
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
///
/// // `f16::NAN` could be positive or negative, which will affect the sort order.
/// if f16::NAN.is_sign_negative() {
/// bois.into_iter().map(|b| b.weight)
/// .zip([f16::NAN, -5.0, 0.1, 10.0, 99.0, f16::INFINITY].iter())
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
/// } else {
/// bois.into_iter().map(|b| b.weight)
/// .zip([-5.0, 0.1, 10.0, 99.0, f16::INFINITY, f16::NAN].iter())
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
/// }
/// ```
#[inline]
#[must_use]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
let mut left = self.to_bits() as i16;
let mut right = other.to_bits() as i16;
// In case of negatives, flip all the bits except the sign
// to achieve a similar layout as two's complement integers
//
// Why does this work? IEEE 754 floats consist of three fields:
// Sign bit, exponent and mantissa. The set of exponent and mantissa
// fields as a whole have the property that their bitwise order is
// equal to the numeric magnitude where the magnitude is defined.
// The magnitude is not normally defined on NaN values, but
// IEEE 754 totalOrder defines the NaN values also to follow the
// bitwise order. This leads to order explained in the doc comment.
// However, the representation of magnitude is the same for negative
// and positive numbers only the sign bit is different.
// To easily compare the floats as signed integers, we need to
// flip the exponent and mantissa bits in case of negative numbers.
// We effectively convert the numbers to "two's complement" form.
//
// To do the flipping, we construct a mask and XOR against it.
// We branchlessly calculate an "all-ones except for the sign bit"
// mask from negative-signed values: right shifting sign-extends
// the integer, so we "fill" the mask with sign bits, and then
// convert to unsigned to push one more zero bit.
// On positive values, the mask is all zeros, so it's a no-op.
left ^= (((left >> 15) as u16) >> 1) as i16;
right ^= (((right >> 15) as u16) >> 1) as i16;
left.cmp(&right)
}
/// Restrict a value to a certain interval unless it is NaN.
///
/// Returns `max` if `self` is greater than `max`, and `min` if `self` is
/// less than `min`. Otherwise this returns `self`.
///
/// Note that this function returns NaN if the initial value was NaN as
/// well.
///
/// # Panics
///
/// Panics if `min > max`, `min` is NaN, or `max` is NaN.
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
///
/// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0);
/// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0);
/// assert!((2.0f16).clamp(-2.0, 1.0) == 1.0);
/// assert!((f16::NAN).clamp(-2.0, 1.0).is_nan());
/// # }
/// ```
#[inline]
#[cfg(not(bootstrap))]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn clamp(mut self, min: f16, max: f16) -> f16 {
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
if self < min {
self = min;
}
if self > max {
self = max;
}
self
}
}

View File

@ -32,4 +32,34 @@ impl f128 {
pub fn powi(self, n: i32) -> f128 {
unsafe { intrinsics::powif128(self, n) }
}
/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128
///
/// 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]
#[cfg(not(bootstrap))]
#[rustc_allow_incoherent_impl]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub 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))
}
}

View File

@ -32,4 +32,33 @@ impl f16 {
pub fn powi(self, n: i32) -> f16 {
unsafe { intrinsics::powif16(self, n) }
}
/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// # #[cfg(reliable_f16)] {
///
/// 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]
#[cfg(not(bootstrap))]
#[rustc_allow_incoherent_impl]
#[unstable(feature = "f16", issue = "116909")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn abs(self) -> Self {
// FIXME(f16_f128): replace with `intrinsics::fabsf16` when available
Self::from_bits(self.to_bits() & !(1 << 15))
}
}