Skip to content

Commit 9a967de

Browse files
authored
Rollup merge of #148690 - IntegralPilot:clamp-mag, r=joboet
Implement `clamp_magnitude` method for primitive floats & signed integers Tracking issue #148519 ACP rust-lang/libs-team#686
2 parents 9b82a4f + ae7fa32 commit 9a967de

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
@@ -1291,6 +1291,38 @@ impl f128 {
12911291
self
12921292
}
12931293

1294+
/// Clamps this number to a symmetric range centered around zero.
1295+
///
1296+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1297+
///
1298+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1299+
/// explicit about the intent.
1300+
///
1301+
/// # Panics
1302+
///
1303+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1304+
///
1305+
/// # Examples
1306+
///
1307+
/// ```
1308+
/// #![feature(f128)]
1309+
/// #![feature(clamp_magnitude)]
1310+
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
1311+
/// assert_eq!(5.0f128.clamp_magnitude(3.0), 3.0);
1312+
/// assert_eq!((-5.0f128).clamp_magnitude(3.0), -3.0);
1313+
/// assert_eq!(2.0f128.clamp_magnitude(3.0), 2.0);
1314+
/// assert_eq!((-2.0f128).clamp_magnitude(3.0), -2.0);
1315+
/// # }
1316+
/// ```
1317+
#[inline]
1318+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1319+
#[must_use = "this returns the clamped value and does not modify the original"]
1320+
pub fn clamp_magnitude(self, limit: f128) -> f128 {
1321+
assert!(limit >= 0.0, "limit must be non-negative");
1322+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1323+
self.clamp(-limit, limit)
1324+
}
1325+
12941326
/// Computes the absolute value of `self`.
12951327
///
12961328
/// 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
@@ -1269,6 +1269,38 @@ impl f16 {
12691269
self
12701270
}
12711271

1272+
/// Clamps this number to a symmetric range centered around zero.
1273+
///
1274+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1275+
///
1276+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1277+
/// explicit about the intent.
1278+
///
1279+
/// # Panics
1280+
///
1281+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1282+
///
1283+
/// # Examples
1284+
///
1285+
/// ```
1286+
/// #![feature(f16)]
1287+
/// #![feature(clamp_magnitude)]
1288+
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
1289+
/// assert_eq!(5.0f16.clamp_magnitude(3.0), 3.0);
1290+
/// assert_eq!((-5.0f16).clamp_magnitude(3.0), -3.0);
1291+
/// assert_eq!(2.0f16.clamp_magnitude(3.0), 2.0);
1292+
/// assert_eq!((-2.0f16).clamp_magnitude(3.0), -2.0);
1293+
/// # }
1294+
/// ```
1295+
#[inline]
1296+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1297+
#[must_use = "this returns the clamped value and does not modify the original"]
1298+
pub fn clamp_magnitude(self, limit: f16) -> f16 {
1299+
assert!(limit >= 0.0, "limit must be non-negative");
1300+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1301+
self.clamp(-limit, limit)
1302+
}
1303+
12721304
/// Computes the absolute value of `self`.
12731305
///
12741306
/// 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
@@ -1446,6 +1446,35 @@ impl f32 {
14461446
self
14471447
}
14481448

1449+
/// Clamps this number to a symmetric range centered around zero.
1450+
///
1451+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1452+
///
1453+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1454+
/// explicit about the intent.
1455+
///
1456+
/// # Panics
1457+
///
1458+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1459+
///
1460+
/// # Examples
1461+
///
1462+
/// ```
1463+
/// #![feature(clamp_magnitude)]
1464+
/// assert_eq!(5.0f32.clamp_magnitude(3.0), 3.0);
1465+
/// assert_eq!((-5.0f32).clamp_magnitude(3.0), -3.0);
1466+
/// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0);
1467+
/// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0);
1468+
/// ```
1469+
#[must_use = "this returns the clamped value and does not modify the original"]
1470+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1471+
#[inline]
1472+
pub fn clamp_magnitude(self, limit: f32) -> f32 {
1473+
assert!(limit >= 0.0, "limit must be non-negative");
1474+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1475+
self.clamp(-limit, limit)
1476+
}
1477+
14491478
/// Computes the absolute value of `self`.
14501479
///
14511480
/// 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
@@ -1444,6 +1444,35 @@ impl f64 {
14441444
self
14451445
}
14461446

1447+
/// Clamps this number to a symmetric range centered around zero.
1448+
///
1449+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
1450+
///
1451+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
1452+
/// explicit about the intent.
1453+
///
1454+
/// # Panics
1455+
///
1456+
/// Panics if `limit` is negative or NaN, as this indicates a logic error.
1457+
///
1458+
/// # Examples
1459+
///
1460+
/// ```
1461+
/// #![feature(clamp_magnitude)]
1462+
/// assert_eq!(5.0f64.clamp_magnitude(3.0), 3.0);
1463+
/// assert_eq!((-5.0f64).clamp_magnitude(3.0), -3.0);
1464+
/// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0);
1465+
/// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0);
1466+
/// ```
1467+
#[must_use = "this returns the clamped value and does not modify the original"]
1468+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
1469+
#[inline]
1470+
pub fn clamp_magnitude(self, limit: f64) -> f64 {
1471+
assert!(limit >= 0.0, "limit must be non-negative");
1472+
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
1473+
self.clamp(-limit, limit)
1474+
}
1475+
14471476
/// Computes the absolute value of `self`.
14481477
///
14491478
/// 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
@@ -3878,5 +3878,32 @@ macro_rules! int_impl {
38783878
pub const fn max_value() -> Self {
38793879
Self::MAX
38803880
}
3881+
3882+
/// Clamps this number to a symmetric range centred around zero.
3883+
///
3884+
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
3885+
///
3886+
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
3887+
/// explicit about the intent.
3888+
///
3889+
/// # Examples
3890+
///
3891+
/// ```
3892+
/// #![feature(clamp_magnitude)]
3893+
#[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")]
3894+
#[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")]
3895+
#[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")]
3896+
#[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")]
3897+
/// ```
3898+
#[must_use = "this returns the clamped value and does not modify the original"]
3899+
#[unstable(feature = "clamp_magnitude", issue = "148519")]
3900+
#[inline]
3901+
pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self {
3902+
if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) {
3903+
self.clamp(-limit, limit)
3904+
} else {
3905+
self
3906+
}
3907+
}
38813908
}
38823909
}

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#![feature(cfg_target_has_reliable_f16_f128)]
1717
#![feature(char_internals)]
1818
#![feature(char_max_len)]
19+
#![feature(clamp_magnitude)]
1920
#![feature(clone_to_uninit)]
2021
#![feature(const_array)]
2122
#![feature(const_cell_traits)]
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)