Implement `clamp_magnitude` for floats & signed integers
Added feature gate, documentation and tests also.
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index e710153..f51b561 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -1276,6 +1276,38 @@ pub const fn clamp(mut self, min: f128, max: f128) -> 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.
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index aa8342a..318c959 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -1254,6 +1254,38 @@ pub const fn clamp(mut self, min: f16, max: f16) -> 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.
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 3070e1d..91e276c 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -1431,6 +1431,35 @@ pub const fn clamp(mut self, min: f32, max: f32) -> 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.
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index dc8ccc5..a4b2979 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -1429,6 +1429,35 @@ pub const fn clamp(mut self, min: f64, max: f64) -> 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.
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 7d395eb..9134d37 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -3855,5 +3855,32 @@ pub const fn min_value() -> Self {
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
+ }
+ }
}
}
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index 80b6203..124a8cf 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -15,6 +15,7 @@
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(char_internals)]
#![feature(char_max_len)]
+#![feature(clamp_magnitude)]
#![feature(clone_to_uninit)]
#![feature(const_cell_traits)]
#![feature(const_cmp)]
diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs
new file mode 100644
index 0000000..0f96e55
--- /dev/null
+++ b/library/coretests/tests/num/clamp_magnitude.rs
@@ -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);
+}