about summary refs log tree commit diff
path: root/library
diff options
context:
space:
mode:
Diffstat (limited to 'library')
-rw-r--r--library/core/src/num/f128.rs640
-rw-r--r--library/core/src/num/f16.rs612
-rw-r--r--library/std/src/f128.rs30
-rw-r--r--library/std/src/f16.rs29
4 files changed, 1309 insertions, 2 deletions
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index 490d7bd504d..58ed98c888c 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -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
+    }
 }
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index 4b0c25fcce9..3c58b0af9c2 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -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
+    }
 }
diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs
index 491235a872e..0591c6f517b 100644
--- a/library/std/src/f128.rs
+++ b/library/std/src/f128.rs
@@ -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))
+    }
 }
diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs
index 1cb655ffabd..d4851862299 100644
--- a/library/std/src/f16.rs
+++ b/library/std/src/f16.rs
@@ -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))
+    }
 }