Rollup merge of #148690 - IntegralPilot:clamp-mag, r=joboet

Implement `clamp_magnitude` method for primitive floats & signed integers

Tracking issue rust-lang/rust#148519
ACP https://github.com/rust-lang/libs-team/issues/686
This commit is contained in:
Matthias Krüger 2025-12-01 17:55:05 +01:00 committed by GitHub
commit 9a967de929
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 289 additions and 0 deletions

View file

@ -1291,6 +1291,38 @@ impl f128 {
self
}
/// Clamps this number to a symmetric range centered around zero.
///
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
///
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
/// explicit about the intent.
///
/// # Panics
///
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
///
/// # Examples
///
/// ```
/// #![feature(f128)]
/// #![feature(clamp_magnitude)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
/// assert_eq!(5.0f128.clamp_magnitude(3.0), 3.0);
/// assert_eq!((-5.0f128).clamp_magnitude(3.0), -3.0);
/// assert_eq!(2.0f128.clamp_magnitude(3.0), 2.0);
/// assert_eq!((-2.0f128).clamp_magnitude(3.0), -2.0);
/// # }
/// ```
#[inline]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "this returns the clamped value and does not modify the original"]
pub fn clamp_magnitude(self, limit: f128) -> f128 {
assert!(limit >= 0.0, "limit must be non-negative");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
}
/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.

View file

@ -1269,6 +1269,38 @@ impl f16 {
self
}
/// Clamps this number to a symmetric range centered around zero.
///
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
///
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
/// explicit about the intent.
///
/// # Panics
///
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
///
/// # Examples
///
/// ```
/// #![feature(f16)]
/// #![feature(clamp_magnitude)]
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
/// assert_eq!(5.0f16.clamp_magnitude(3.0), 3.0);
/// assert_eq!((-5.0f16).clamp_magnitude(3.0), -3.0);
/// assert_eq!(2.0f16.clamp_magnitude(3.0), 2.0);
/// assert_eq!((-2.0f16).clamp_magnitude(3.0), -2.0);
/// # }
/// ```
#[inline]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "this returns the clamped value and does not modify the original"]
pub fn clamp_magnitude(self, limit: f16) -> f16 {
assert!(limit >= 0.0, "limit must be non-negative");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
}
/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.

View file

@ -1446,6 +1446,35 @@ impl f32 {
self
}
/// Clamps this number to a symmetric range centered around zero.
///
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
///
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
/// explicit about the intent.
///
/// # Panics
///
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
///
/// # Examples
///
/// ```
/// #![feature(clamp_magnitude)]
/// assert_eq!(5.0f32.clamp_magnitude(3.0), 3.0);
/// assert_eq!((-5.0f32).clamp_magnitude(3.0), -3.0);
/// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0);
/// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0);
/// ```
#[must_use = "this returns the clamped value and does not modify the original"]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[inline]
pub fn clamp_magnitude(self, limit: f32) -> f32 {
assert!(limit >= 0.0, "limit must be non-negative");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
}
/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.

View file

@ -1444,6 +1444,35 @@ impl f64 {
self
}
/// Clamps this number to a symmetric range centered around zero.
///
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
///
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
/// explicit about the intent.
///
/// # Panics
///
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
///
/// # Examples
///
/// ```
/// #![feature(clamp_magnitude)]
/// assert_eq!(5.0f64.clamp_magnitude(3.0), 3.0);
/// assert_eq!((-5.0f64).clamp_magnitude(3.0), -3.0);
/// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0);
/// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0);
/// ```
#[must_use = "this returns the clamped value and does not modify the original"]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[inline]
pub fn clamp_magnitude(self, limit: f64) -> f64 {
assert!(limit >= 0.0, "limit must be non-negative");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
}
/// Computes the absolute value of `self`.
///
/// This function always returns the precise result.

View file

@ -3878,5 +3878,32 @@ macro_rules! int_impl {
pub const fn max_value() -> Self {
Self::MAX
}
/// Clamps this number to a symmetric range centred around zero.
///
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
///
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
/// explicit about the intent.
///
/// # Examples
///
/// ```
/// #![feature(clamp_magnitude)]
#[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")]
#[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")]
#[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")]
#[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")]
/// ```
#[must_use = "this returns the clamped value and does not modify the original"]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[inline]
pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self {
if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) {
self.clamp(-limit, limit)
} else {
self
}
}
}
}

View file

@ -16,6 +16,7 @@
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(char_internals)]
#![feature(char_max_len)]
#![feature(clamp_magnitude)]
#![feature(clone_to_uninit)]
#![feature(const_array)]
#![feature(const_cell_traits)]

View file

@ -0,0 +1,139 @@
macro_rules! check_int_clamp {
($t:ty, $ut:ty) => {
let min = <$t>::MIN;
let max = <$t>::MAX;
let max_u = <$ut>::MAX;
// Basic clamping
assert_eq!((100 as $t).clamp_magnitude(50), 50);
assert_eq!((-100 as $t).clamp_magnitude(50), -50);
assert_eq!((30 as $t).clamp_magnitude(50), 30);
assert_eq!((-30 as $t).clamp_magnitude(50), -30);
// Exact boundary
assert_eq!((50 as $t).clamp_magnitude(50), 50);
assert_eq!((-50 as $t).clamp_magnitude(50), -50);
// Zero cases
assert_eq!((0 as $t).clamp_magnitude(100), 0);
assert_eq!((0 as $t).clamp_magnitude(0), 0);
assert_eq!((100 as $t).clamp_magnitude(0), 0);
assert_eq!((-100 as $t).clamp_magnitude(0), 0);
// MIN/MAX values
// Symmetric range [-MAX, MAX]
assert_eq!(max.clamp_magnitude(max as $ut), max);
assert_eq!(min.clamp_magnitude(max as $ut), -max);
// Full range (limit covers MIN)
let min_abs = min.unsigned_abs();
assert_eq!(min.clamp_magnitude(min_abs), min);
// Limit larger than type max (uN > iN::MAX)
assert_eq!(max.clamp_magnitude(max_u), max);
assert_eq!(min.clamp_magnitude(max_u), min);
};
}
#[test]
fn test_clamp_magnitude_i8() {
check_int_clamp!(i8, u8);
}
#[test]
fn test_clamp_magnitude_i16() {
check_int_clamp!(i16, u16);
}
#[test]
fn test_clamp_magnitude_i32() {
check_int_clamp!(i32, u32);
}
#[test]
fn test_clamp_magnitude_i64() {
check_int_clamp!(i64, u64);
}
#[test]
fn test_clamp_magnitude_i128() {
check_int_clamp!(i128, u128);
}
#[test]
fn test_clamp_magnitude_isize() {
check_int_clamp!(isize, usize);
}
macro_rules! check_float_clamp {
($t:ty) => {
// Basic clamping
assert_eq!((5.0 as $t).clamp_magnitude(3.0), 3.0);
assert_eq!((-5.0 as $t).clamp_magnitude(3.0), -3.0);
assert_eq!((2.0 as $t).clamp_magnitude(3.0), 2.0);
assert_eq!((-2.0 as $t).clamp_magnitude(3.0), -2.0);
// Exact boundary
assert_eq!((3.0 as $t).clamp_magnitude(3.0), 3.0);
assert_eq!((-3.0 as $t).clamp_magnitude(3.0), -3.0);
// Zero cases
assert_eq!((0.0 as $t).clamp_magnitude(1.0), 0.0);
assert_eq!((-0.0 as $t).clamp_magnitude(1.0), 0.0);
assert_eq!((5.0 as $t).clamp_magnitude(0.0), 0.0);
assert_eq!((-5.0 as $t).clamp_magnitude(0.0), 0.0);
// Special values - Infinity
let inf = <$t>::INFINITY;
let neg_inf = <$t>::NEG_INFINITY;
assert_eq!(inf.clamp_magnitude(100.0), 100.0);
assert_eq!(neg_inf.clamp_magnitude(100.0), -100.0);
assert_eq!(inf.clamp_magnitude(inf), inf);
// Value with infinite limit
assert_eq!((1.0 as $t).clamp_magnitude(inf), 1.0);
assert_eq!((-1.0 as $t).clamp_magnitude(inf), -1.0);
// MIN and MAX
let max = <$t>::MAX;
let min = <$t>::MIN;
// Large limit
let huge = 1e30;
assert_eq!(max.clamp_magnitude(huge), huge);
assert_eq!(min.clamp_magnitude(huge), -huge);
};
}
#[test]
fn test_clamp_magnitude_f32() {
check_float_clamp!(f32);
}
#[test]
fn test_clamp_magnitude_f64() {
check_float_clamp!(f64);
}
#[test]
#[should_panic(expected = "limit must be non-negative")]
fn test_clamp_magnitude_f32_panic_negative_limit() {
let _ = 1.0f32.clamp_magnitude(-1.0);
}
#[test]
#[should_panic(expected = "limit must be non-negative")]
fn test_clamp_magnitude_f64_panic_negative_limit() {
let _ = 1.0f64.clamp_magnitude(-1.0);
}
#[test]
#[should_panic]
fn test_clamp_magnitude_f32_panic_nan_limit() {
let _ = 1.0f32.clamp_magnitude(f32::NAN);
}
#[test]
#[should_panic]
fn test_clamp_magnitude_f64_panic_nan_limit() {
let _ = 1.0f64.clamp_magnitude(f64::NAN);
}