Skip to content

Commit 1a72d7c

Browse files
committed
Implement midpoint for all signed and unsigned integers
1 parent 23a76a8 commit 1a72d7c

File tree

6 files changed

+151
-0
lines changed

6 files changed

+151
-0
lines changed

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
#![feature(const_maybe_uninit_assume_init)]
133133
#![feature(const_maybe_uninit_uninit_array)]
134134
#![feature(const_nonnull_new)]
135+
#![feature(const_num_midpoint)]
135136
#![feature(const_option)]
136137
#![feature(const_option_ext)]
137138
#![feature(const_pin)]

library/core/src/num/int_macros.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,6 +2332,44 @@ macro_rules! int_impl {
23322332
}
23332333
}
23342334

2335+
/// Calculates the middle point of `self` and `rhs`.
2336+
///
2337+
/// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a
2338+
/// sufficiently-large signed integral type. This implies that the result is
2339+
/// always rounded towards negative infinity and that no overflow will ever occur.
2340+
///
2341+
/// # Examples
2342+
///
2343+
/// ```
2344+
/// #![feature(num_midpoint)]
2345+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")]
2346+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-1), -1);")]
2347+
#[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(0), -1);")]
2348+
/// ```
2349+
#[unstable(feature = "num_midpoint", issue = "110840")]
2350+
#[rustc_const_unstable(feature = "const_num_midpoint", issue = "110840")]
2351+
#[rustc_allow_const_fn_unstable(const_num_midpoint)]
2352+
#[must_use = "this returns the result of the operation, \
2353+
without modifying the original"]
2354+
#[inline]
2355+
pub const fn midpoint(self, rhs: Self) -> Self {
2356+
const U: $UnsignedT = <$SelfT>::MIN.unsigned_abs();
2357+
2358+
// Map an $SelfT to an $UnsignedT
2359+
// ex: i8 [-128; 127] to [0; 255]
2360+
const fn map(a: $SelfT) -> $UnsignedT {
2361+
(a as $UnsignedT) ^ U
2362+
}
2363+
2364+
// Map an $UnsignedT to an $SelfT
2365+
// ex: u8 [0; 255] to [-128; 127]
2366+
const fn demap(a: $UnsignedT) -> $SelfT {
2367+
(a ^ U) as $SelfT
2368+
}
2369+
2370+
demap(<$UnsignedT>::midpoint(map(self), map(rhs)))
2371+
}
2372+
23352373
/// Returns the logarithm of the number with respect to an arbitrary base,
23362374
/// rounded down.
23372375
///

library/core/src/num/mod.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,57 @@ depending on the target pointer size.
9595
};
9696
}
9797

98+
macro_rules! midpoint_impl {
99+
($SelfT:ty, unsigned) => {
100+
/// Calculates the middle point of `self` and `rhs`.
101+
///
102+
/// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a
103+
/// sufficiently-large signed integral type. This implies that the result is
104+
/// always rounded towards negative infinity and that no overflow will ever occur.
105+
///
106+
/// # Examples
107+
///
108+
/// ```
109+
/// #![feature(num_midpoint)]
110+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")]
111+
#[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")]
112+
/// ```
113+
#[unstable(feature = "num_midpoint", issue = "110840")]
114+
#[rustc_const_unstable(feature = "const_num_midpoint", issue = "110840")]
115+
#[must_use = "this returns the result of the operation, \
116+
without modifying the original"]
117+
#[inline]
118+
pub const fn midpoint(self, rhs: $SelfT) -> $SelfT {
119+
// Use the well known branchless algorthim from Hacker's Delight to compute
120+
// `(a + b) / 2` without overflowing: `((a ^ b) >> 1) + (a & b)`.
121+
((self ^ rhs) >> 1) + (self & rhs)
122+
}
123+
};
124+
($SelfT:ty, $WideT:ty, unsigned) => {
125+
/// Calculates the middle point of `self` and `rhs`.
126+
///
127+
/// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a
128+
/// sufficiently-large signed integral type. This implies that the result is
129+
/// always rounded towards negative infinity and that no overflow will ever occur.
130+
///
131+
/// # Examples
132+
///
133+
/// ```
134+
/// #![feature(num_midpoint)]
135+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")]
136+
#[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")]
137+
/// ```
138+
#[unstable(feature = "num_midpoint", issue = "110840")]
139+
#[rustc_const_unstable(feature = "const_num_midpoint", issue = "110840")]
140+
#[must_use = "this returns the result of the operation, \
141+
without modifying the original"]
142+
#[inline]
143+
pub const fn midpoint(self, rhs: $SelfT) -> $SelfT {
144+
((self as $WideT + rhs as $WideT) / 2) as $SelfT
145+
}
146+
};
147+
}
148+
98149
macro_rules! widening_impl {
99150
($SelfT:ty, $WideT:ty, $BITS:literal, unsigned) => {
100151
/// Calculates the complete product `self * rhs` without the possibility to overflow.
@@ -455,6 +506,7 @@ impl u8 {
455506
bound_condition = "",
456507
}
457508
widening_impl! { u8, u16, 8, unsigned }
509+
midpoint_impl! { u8, u16, unsigned }
458510

459511
/// Checks if the value is within the ASCII range.
460512
///
@@ -1057,6 +1109,7 @@ impl u16 {
10571109
bound_condition = "",
10581110
}
10591111
widening_impl! { u16, u32, 16, unsigned }
1112+
midpoint_impl! { u16, u32, unsigned }
10601113

10611114
/// Checks if the value is a Unicode surrogate code point, which are disallowed values for [`char`].
10621115
///
@@ -1105,6 +1158,7 @@ impl u32 {
11051158
bound_condition = "",
11061159
}
11071160
widening_impl! { u32, u64, 32, unsigned }
1161+
midpoint_impl! { u32, u64, unsigned }
11081162
}
11091163

11101164
impl u64 {
@@ -1128,6 +1182,7 @@ impl u64 {
11281182
bound_condition = "",
11291183
}
11301184
widening_impl! { u64, u128, 64, unsigned }
1185+
midpoint_impl! { u64, u128, unsigned }
11311186
}
11321187

11331188
impl u128 {
@@ -1152,6 +1207,7 @@ impl u128 {
11521207
from_xe_bytes_doc = "",
11531208
bound_condition = "",
11541209
}
1210+
midpoint_impl! { u128, unsigned }
11551211
}
11561212

11571213
#[cfg(target_pointer_width = "16")]
@@ -1176,6 +1232,7 @@ impl usize {
11761232
bound_condition = " on 16-bit targets",
11771233
}
11781234
widening_impl! { usize, u32, 16, unsigned }
1235+
midpoint_impl! { usize, u32, unsigned }
11791236
}
11801237

11811238
#[cfg(target_pointer_width = "32")]
@@ -1200,6 +1257,7 @@ impl usize {
12001257
bound_condition = " on 32-bit targets",
12011258
}
12021259
widening_impl! { usize, u64, 32, unsigned }
1260+
midpoint_impl! { usize, u64, unsigned }
12031261
}
12041262

12051263
#[cfg(target_pointer_width = "64")]
@@ -1224,6 +1282,7 @@ impl usize {
12241282
bound_condition = " on 64-bit targets",
12251283
}
12261284
widening_impl! { usize, u128, 64, unsigned }
1285+
midpoint_impl! { usize, u128, unsigned }
12271286
}
12281287

12291288
impl usize {

library/core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#![feature(maybe_uninit_uninit_array_transpose)]
5555
#![feature(min_specialization)]
5656
#![feature(numfmt)]
57+
#![feature(num_midpoint)]
5758
#![feature(step_trait)]
5859
#![feature(str_internals)]
5960
#![feature(std_internals)]

library/core/tests/num/int_macros.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,32 @@ macro_rules! int_module {
364364
assert_eq!((0 as $T).borrowing_sub($T::MIN, false), ($T::MIN, true));
365365
assert_eq!((0 as $T).borrowing_sub($T::MIN, true), ($T::MAX, false));
366366
}
367+
368+
#[test]
369+
fn test_midpoint() {
370+
assert_eq!(<$T>::midpoint(1, 3), 2);
371+
assert_eq!(<$T>::midpoint(3, 1), 2);
372+
373+
assert_eq!(<$T>::midpoint(0, 0), 0);
374+
assert_eq!(<$T>::midpoint(0, 2), 1);
375+
assert_eq!(<$T>::midpoint(2, 0), 1);
376+
assert_eq!(<$T>::midpoint(2, 2), 2);
377+
378+
assert_eq!(<$T>::midpoint(1, 4), 2);
379+
assert_eq!(<$T>::midpoint(4, 1), 2);
380+
assert_eq!(<$T>::midpoint(3, 4), 3);
381+
assert_eq!(<$T>::midpoint(4, 3), 3);
382+
383+
assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), -1);
384+
assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), -1);
385+
assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN);
386+
assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX);
387+
388+
assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3);
389+
assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3);
390+
assert_eq!(<$T>::midpoint(<$T>::MAX, 6), <$T>::MAX / 2 + 3);
391+
assert_eq!(<$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3);
392+
}
367393
}
368394
};
369395
}

library/core/tests/num/uint_macros.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,32 @@ macro_rules! uint_module {
252252
assert_eq!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false));
253253
assert_eq!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true));
254254
}
255+
256+
#[test]
257+
fn test_midpoint() {
258+
assert_eq!(<$T>::midpoint(1, 3), 2);
259+
assert_eq!(<$T>::midpoint(3, 1), 2);
260+
261+
assert_eq!(<$T>::midpoint(0, 0), 0);
262+
assert_eq!(<$T>::midpoint(0, 2), 1);
263+
assert_eq!(<$T>::midpoint(2, 0), 1);
264+
assert_eq!(<$T>::midpoint(2, 2), 2);
265+
266+
assert_eq!(<$T>::midpoint(1, 4), 2);
267+
assert_eq!(<$T>::midpoint(4, 1), 2);
268+
assert_eq!(<$T>::midpoint(3, 4), 3);
269+
assert_eq!(<$T>::midpoint(4, 3), 3);
270+
271+
assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2);
272+
assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), (<$T>::MAX - <$T>::MIN) / 2);
273+
assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN);
274+
assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX);
275+
276+
assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3);
277+
assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3);
278+
assert_eq!(<$T>::midpoint(<$T>::MAX, 6), (<$T>::MAX - <$T>::MIN) / 2 + 3);
279+
assert_eq!(<$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3);
280+
}
255281
}
256282
};
257283
}

0 commit comments

Comments
 (0)