Skip to content

Commit ae7fa32

Browse files
committed
Implement clamp_magnitude for floats & signed integers
Added feature gate, documentation and tests also.
1 parent ceb7df7 commit ae7fa32

File tree

7 files changed

+289
-0
lines changed

7 files changed

+289
-0
lines changed

library/core/src/num/f128.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,38 @@ impl f128 {
12761276
self
12771277
}
12781278

1279+
/// Clamps this number to a symmetric range centered around zero.
1280+
///
1281+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1282+
///
1283+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1284+
/// explicit about the intent.
1285+
///
1286+
/// # Panics
1287+
///
1288+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1289+
///
1290+
/// # Examples
1291+
///
1292+
/// ```
1293+
/// #![feature(f128)]
1294+
/// #![feature(clamp_magnitude)]
1295+
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
1296+
/// assert_eq!(5.0f128.clamp_magnitude(3.0), 3.0);
1297+
/// assert_eq!((-5.0f128).clamp_magnitude(3.0), -3.0);
1298+
/// assert_eq!(2.0f128.clamp_magnitude(3.0), 2.0);
1299+
/// assert_eq!((-2.0f128).clamp_magnitude(3.0), -2.0);
1300+
/// # }
1301+
/// ```
1302+
#[inline]
1303+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1304+
#[must_use = "this returns the clamped value and does not modify the original"]
1305+
pub fn clamp_magnitude(self, limit: f128) -> f128 {
1306+
assert!(limit >= 0.0, "limit must be non-negative");
1307+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1308+
self.clamp(-limit, limit)
1309+
}
1310+
12791311
/// Computes the absolute value of `self`.
12801312
///
12811313
/// This function always returns the precise result.

library/core/src/num/f16.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,38 @@ impl f16 {
12541254
self
12551255
}
12561256

1257+
/// Clamps this number to a symmetric range centered around zero.
1258+
///
1259+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1260+
///
1261+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1262+
/// explicit about the intent.
1263+
///
1264+
/// # Panics
1265+
///
1266+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1267+
///
1268+
/// # Examples
1269+
///
1270+
/// ```
1271+
/// #![feature(f16)]
1272+
/// #![feature(clamp_magnitude)]
1273+
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
1274+
/// assert_eq!(5.0f16.clamp_magnitude(3.0), 3.0);
1275+
/// assert_eq!((-5.0f16).clamp_magnitude(3.0), -3.0);
1276+
/// assert_eq!(2.0f16.clamp_magnitude(3.0), 2.0);
1277+
/// assert_eq!((-2.0f16).clamp_magnitude(3.0), -2.0);
1278+
/// # }
1279+
/// ```
1280+
#[inline]
1281+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1282+
#[must_use = "this returns the clamped value and does not modify the original"]
1283+
pub fn clamp_magnitude(self, limit: f16) -> f16 {
1284+
assert!(limit >= 0.0, "limit must be non-negative");
1285+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1286+
self.clamp(-limit, limit)
1287+
}
1288+
12571289
/// Computes the absolute value of `self`.
12581290
///
12591291
/// This function always returns the precise result.

library/core/src/num/f32.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,35 @@ impl f32 {
14311431
self
14321432
}
14331433

1434+
/// Clamps this number to a symmetric range centered around zero.
1435+
///
1436+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1437+
///
1438+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1439+
/// explicit about the intent.
1440+
///
1441+
/// # Panics
1442+
///
1443+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1444+
///
1445+
/// # Examples
1446+
///
1447+
/// ```
1448+
/// #![feature(clamp_magnitude)]
1449+
/// assert_eq!(5.0f32.clamp_magnitude(3.0), 3.0);
1450+
/// assert_eq!((-5.0f32).clamp_magnitude(3.0), -3.0);
1451+
/// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0);
1452+
/// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0);
1453+
/// ```
1454+
#[must_use = "this returns the clamped value and does not modify the original"]
1455+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1456+
#[inline]
1457+
pub fn clamp_magnitude(self, limit: f32) -> f32 {
1458+
assert!(limit >= 0.0, "limit must be non-negative");
1459+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1460+
self.clamp(-limit, limit)
1461+
}
1462+
14341463
/// Computes the absolute value of `self`.
14351464
///
14361465
/// This function always returns the precise result.

library/core/src/num/f64.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,35 @@ impl f64 {
14291429
self
14301430
}
14311431

1432+
/// Clamps this number to a symmetric range centered around zero.
1433+
///
1434+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1435+
///
1436+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1437+
/// explicit about the intent.
1438+
///
1439+
/// # Panics
1440+
///
1441+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1442+
///
1443+
/// # Examples
1444+
///
1445+
/// ```
1446+
/// #![feature(clamp_magnitude)]
1447+
/// assert_eq!(5.0f64.clamp_magnitude(3.0), 3.0);
1448+
/// assert_eq!((-5.0f64).clamp_magnitude(3.0), -3.0);
1449+
/// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0);
1450+
/// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0);
1451+
/// ```
1452+
#[must_use = "this returns the clamped value and does not modify the original"]
1453+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1454+
#[inline]
1455+
pub fn clamp_magnitude(self, limit: f64) -> f64 {
1456+
assert!(limit >= 0.0, "limit must be non-negative");
1457+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1458+
self.clamp(-limit, limit)
1459+
}
1460+
14321461
/// Computes the absolute value of `self`.
14331462
///
14341463
/// This function always returns the precise result.

library/core/src/num/int_macros.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3855,5 +3855,32 @@ macro_rules! int_impl {
38553855
pub const fn max_value() -> Self {
38563856
Self::MAX
38573857
}
3858+
3859+
/// Clamps this number to a symmetric range centred around zero.
3860+
///
3861+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
3862+
///
3863+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
3864+
/// explicit about the intent.
3865+
///
3866+
/// # Examples
3867+
///
3868+
/// ```
3869+
/// #![feature(clamp_magnitude)]
3870+
#[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")]
3871+
#[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")]
3872+
#[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")]
3873+
#[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")]
3874+
/// ```
3875+
#[must_use = "this returns the clamped value and does not modify the original"]
3876+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
3877+
#[inline]
3878+
pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self {
3879+
if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) {
3880+
self.clamp(-limit, limit)
3881+
} else {
3882+
self
3883+
}
3884+
}
38583885
}
38593886
}

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#![feature(cfg_target_has_reliable_f16_f128)]
1616
#![feature(char_internals)]
1717
#![feature(char_max_len)]
18+
#![feature(clamp_magnitude)]
1819
#![feature(clone_to_uninit)]
1920
#![feature(const_cell_traits)]
2021
#![feature(const_cmp)]
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
macro_rules! check_int_clamp {
2+
($t:ty, $ut:ty) => {
3+
let min = <$t>::MIN;
4+
let max = <$t>::MAX;
5+
let max_u = <$ut>::MAX;
6+
7+
// Basic clamping
8+
assert_eq!((100 as $t).clamp_magnitude(50), 50);
9+
assert_eq!((-100 as $t).clamp_magnitude(50), -50);
10+
assert_eq!((30 as $t).clamp_magnitude(50), 30);
11+
assert_eq!((-30 as $t).clamp_magnitude(50), -30);
12+
13+
// Exact boundary
14+
assert_eq!((50 as $t).clamp_magnitude(50), 50);
15+
assert_eq!((-50 as $t).clamp_magnitude(50), -50);
16+
17+
// Zero cases
18+
assert_eq!((0 as $t).clamp_magnitude(100), 0);
19+
assert_eq!((0 as $t).clamp_magnitude(0), 0);
20+
assert_eq!((100 as $t).clamp_magnitude(0), 0);
21+
assert_eq!((-100 as $t).clamp_magnitude(0), 0);
22+
23+
// MIN/MAX values
24+
// Symmetric range [-MAX, MAX]
25+
assert_eq!(max.clamp_magnitude(max as $ut), max);
26+
assert_eq!(min.clamp_magnitude(max as $ut), -max);
27+
28+
// Full range (limit covers MIN)
29+
let min_abs = min.unsigned_abs();
30+
assert_eq!(min.clamp_magnitude(min_abs), min);
31+
32+
// Limit larger than type max (uN > iN::MAX)
33+
assert_eq!(max.clamp_magnitude(max_u), max);
34+
assert_eq!(min.clamp_magnitude(max_u), min);
35+
};
36+
}
37+
38+
#[test]
39+
fn test_clamp_magnitude_i8() {
40+
check_int_clamp!(i8, u8);
41+
}
42+
43+
#[test]
44+
fn test_clamp_magnitude_i16() {
45+
check_int_clamp!(i16, u16);
46+
}
47+
48+
#[test]
49+
fn test_clamp_magnitude_i32() {
50+
check_int_clamp!(i32, u32);
51+
}
52+
53+
#[test]
54+
fn test_clamp_magnitude_i64() {
55+
check_int_clamp!(i64, u64);
56+
}
57+
58+
#[test]
59+
fn test_clamp_magnitude_i128() {
60+
check_int_clamp!(i128, u128);
61+
}
62+
63+
#[test]
64+
fn test_clamp_magnitude_isize() {
65+
check_int_clamp!(isize, usize);
66+
}
67+
68+
macro_rules! check_float_clamp {
69+
($t:ty) => {
70+
// Basic clamping
71+
assert_eq!((5.0 as $t).clamp_magnitude(3.0), 3.0);
72+
assert_eq!((-5.0 as $t).clamp_magnitude(3.0), -3.0);
73+
assert_eq!((2.0 as $t).clamp_magnitude(3.0), 2.0);
74+
assert_eq!((-2.0 as $t).clamp_magnitude(3.0), -2.0);
75+
76+
// Exact boundary
77+
assert_eq!((3.0 as $t).clamp_magnitude(3.0), 3.0);
78+
assert_eq!((-3.0 as $t).clamp_magnitude(3.0), -3.0);
79+
80+
// Zero cases
81+
assert_eq!((0.0 as $t).clamp_magnitude(1.0), 0.0);
82+
assert_eq!((-0.0 as $t).clamp_magnitude(1.0), 0.0);
83+
assert_eq!((5.0 as $t).clamp_magnitude(0.0), 0.0);
84+
assert_eq!((-5.0 as $t).clamp_magnitude(0.0), 0.0);
85+
86+
// Special values - Infinity
87+
let inf = <$t>::INFINITY;
88+
let neg_inf = <$t>::NEG_INFINITY;
89+
assert_eq!(inf.clamp_magnitude(100.0), 100.0);
90+
assert_eq!(neg_inf.clamp_magnitude(100.0), -100.0);
91+
assert_eq!(inf.clamp_magnitude(inf), inf);
92+
93+
// Value with infinite limit
94+
assert_eq!((1.0 as $t).clamp_magnitude(inf), 1.0);
95+
assert_eq!((-1.0 as $t).clamp_magnitude(inf), -1.0);
96+
97+
// MIN and MAX
98+
let max = <$t>::MAX;
99+
let min = <$t>::MIN;
100+
// Large limit
101+
let huge = 1e30;
102+
assert_eq!(max.clamp_magnitude(huge), huge);
103+
assert_eq!(min.clamp_magnitude(huge), -huge);
104+
};
105+
}
106+
107+
#[test]
108+
fn test_clamp_magnitude_f32() {
109+
check_float_clamp!(f32);
110+
}
111+
112+
#[test]
113+
fn test_clamp_magnitude_f64() {
114+
check_float_clamp!(f64);
115+
}
116+
117+
#[test]
118+
#[should_panic(expected = "limit must be non-negative")]
119+
fn test_clamp_magnitude_f32_panic_negative_limit() {
120+
let _ = 1.0f32.clamp_magnitude(-1.0);
121+
}
122+
123+
#[test]
124+
#[should_panic(expected = "limit must be non-negative")]
125+
fn test_clamp_magnitude_f64_panic_negative_limit() {
126+
let _ = 1.0f64.clamp_magnitude(-1.0);
127+
}
128+
129+
#[test]
130+
#[should_panic]
131+
fn test_clamp_magnitude_f32_panic_nan_limit() {
132+
let _ = 1.0f32.clamp_magnitude(f32::NAN);
133+
}
134+
135+
#[test]
136+
#[should_panic]
137+
fn test_clamp_magnitude_f64_panic_nan_limit() {
138+
let _ = 1.0f64.clamp_magnitude(f64::NAN);
139+
}

0 commit comments

Comments
 (0)