Skip to content

Commit 4612afa

Browse files
committed
Round negative signed integer towards zero in iN::midpoint
Instead of towards negative infinity as is currently the case. This done so that the obvious expectations of `midpoint(a, b) == midpoint(b, a)` and `midpoint(-a, -b) == -midpoint(a, b)` are true, which makes the even more obvious implementation `(a + b) / 2` true. rust-lang#110840 (comment)
1 parent 658e709 commit 4612afa

File tree

3 files changed

+67
-40
lines changed

3 files changed

+67
-40
lines changed

core/src/num/int_macros.rs

-38
Original file line numberDiff line numberDiff line change
@@ -3181,44 +3181,6 @@ macro_rules! int_impl {
31813181
}
31823182
}
31833183

3184-
/// Calculates the middle point of `self` and `rhs`.
3185-
///
3186-
/// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a
3187-
/// sufficiently-large signed integral type. This implies that the result is
3188-
/// always rounded towards negative infinity and that no overflow will ever occur.
3189-
///
3190-
/// # Examples
3191-
///
3192-
/// ```
3193-
/// #![feature(num_midpoint)]
3194-
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")]
3195-
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-1), -1);")]
3196-
#[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(0), -1);")]
3197-
/// ```
3198-
#[unstable(feature = "num_midpoint", issue = "110840")]
3199-
#[rustc_const_unstable(feature = "const_num_midpoint", issue = "110840")]
3200-
#[rustc_allow_const_fn_unstable(const_num_midpoint)]
3201-
#[must_use = "this returns the result of the operation, \
3202-
without modifying the original"]
3203-
#[inline]
3204-
pub const fn midpoint(self, rhs: Self) -> Self {
3205-
const U: $UnsignedT = <$SelfT>::MIN.unsigned_abs();
3206-
3207-
// Map an $SelfT to an $UnsignedT
3208-
// ex: i8 [-128; 127] to [0; 255]
3209-
const fn map(a: $SelfT) -> $UnsignedT {
3210-
(a as $UnsignedT) ^ U
3211-
}
3212-
3213-
// Map an $UnsignedT to an $SelfT
3214-
// ex: u8 [0; 255] to [-128; 127]
3215-
const fn demap(a: $UnsignedT) -> $SelfT {
3216-
(a ^ U) as $SelfT
3217-
}
3218-
3219-
demap(<$UnsignedT>::midpoint(map(self), map(rhs)))
3220-
}
3221-
32223184
/// Returns the logarithm of the number with respect to an arbitrary base,
32233185
/// rounded down.
32243186
///

core/src/num/mod.rs

+65
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,37 @@ macro_rules! midpoint_impl {
124124
((self ^ rhs) >> 1) + (self & rhs)
125125
}
126126
};
127+
($SelfT:ty, signed) => {
128+
/// Calculates the middle point of `self` and `rhs`.
129+
///
130+
/// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a
131+
/// sufficiently-large signed integral type. This implies that the result is
132+
/// always rounded towards zero and that no overflow will ever occur.
133+
///
134+
/// # Examples
135+
///
136+
/// ```
137+
/// #![feature(num_midpoint)]
138+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")]
139+
#[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(2), 0);")]
140+
#[doc = concat!("assert_eq!((-7", stringify!($SelfT), ").midpoint(0), -3);")]
141+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")]
142+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")]
143+
/// ```
144+
#[unstable(feature = "num_midpoint", issue = "110840")]
145+
#[rustc_const_unstable(feature = "const_num_midpoint", issue = "110840")]
146+
#[must_use = "this returns the result of the operation, \
147+
without modifying the original"]
148+
#[inline]
149+
pub const fn midpoint(self, rhs: Self) -> Self {
150+
// Use the well known branchless algorithm from Hacker's Delight to compute
151+
// `(a + b) / 2` without overflowing: `((a ^ b) >> 1) + (a & b)`.
152+
let t = ((self ^ rhs) >> 1) + (self & rhs);
153+
// Except that it fails for integers whose sum is an odd negative number as
154+
// their floor is one less than their average. So we adjust the result.
155+
t + (if t < 0 { 1 } else { 0 } & (self ^ rhs))
156+
}
157+
};
127158
($SelfT:ty, $WideT:ty, unsigned) => {
128159
/// Calculates the middle point of `self` and `rhs`.
129160
///
@@ -147,6 +178,32 @@ macro_rules! midpoint_impl {
147178
((self as $WideT + rhs as $WideT) / 2) as $SelfT
148179
}
149180
};
181+
($SelfT:ty, $WideT:ty, signed) => {
182+
/// Calculates the middle point of `self` and `rhs`.
183+
///
184+
/// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a
185+
/// sufficiently-large signed integral type. This implies that the result is
186+
/// always rounded towards zero and that no overflow will ever occur.
187+
///
188+
/// # Examples
189+
///
190+
/// ```
191+
/// #![feature(num_midpoint)]
192+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")]
193+
#[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(2), 0);")]
194+
#[doc = concat!("assert_eq!((-7", stringify!($SelfT), ").midpoint(0), -3);")]
195+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")]
196+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")]
197+
/// ```
198+
#[unstable(feature = "num_midpoint", issue = "110840")]
199+
#[rustc_const_unstable(feature = "const_num_midpoint", issue = "110840")]
200+
#[must_use = "this returns the result of the operation, \
201+
without modifying the original"]
202+
#[inline]
203+
pub const fn midpoint(self, rhs: $SelfT) -> $SelfT {
204+
((self as $WideT + rhs as $WideT) / 2) as $SelfT
205+
}
206+
};
150207
}
151208

152209
macro_rules! widening_impl {
@@ -300,6 +357,7 @@ impl i8 {
300357
from_xe_bytes_doc = "",
301358
bound_condition = "",
302359
}
360+
midpoint_impl! { i8, i16, signed }
303361
}
304362

305363
impl i16 {
@@ -323,6 +381,7 @@ impl i16 {
323381
from_xe_bytes_doc = "",
324382
bound_condition = "",
325383
}
384+
midpoint_impl! { i16, i32, signed }
326385
}
327386

328387
impl i32 {
@@ -346,6 +405,7 @@ impl i32 {
346405
from_xe_bytes_doc = "",
347406
bound_condition = "",
348407
}
408+
midpoint_impl! { i32, i64, signed }
349409
}
350410

351411
impl i64 {
@@ -369,6 +429,7 @@ impl i64 {
369429
from_xe_bytes_doc = "",
370430
bound_condition = "",
371431
}
432+
midpoint_impl! { i64, i128, signed }
372433
}
373434

374435
impl i128 {
@@ -394,6 +455,7 @@ impl i128 {
394455
from_xe_bytes_doc = "",
395456
bound_condition = "",
396457
}
458+
midpoint_impl! { i128, signed }
397459
}
398460

399461
#[cfg(target_pointer_width = "16")]
@@ -418,6 +480,7 @@ impl isize {
418480
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
419481
bound_condition = " on 16-bit targets",
420482
}
483+
midpoint_impl! { isize, i32, signed }
421484
}
422485

423486
#[cfg(target_pointer_width = "32")]
@@ -442,6 +505,7 @@ impl isize {
442505
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
443506
bound_condition = " on 32-bit targets",
444507
}
508+
midpoint_impl! { isize, i64, signed }
445509
}
446510

447511
#[cfg(target_pointer_width = "64")]
@@ -466,6 +530,7 @@ impl isize {
466530
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
467531
bound_condition = " on 64-bit targets",
468532
}
533+
midpoint_impl! { isize, i128, signed }
469534
}
470535

471536
/// If the 6th bit is set ascii is lower case.

core/tests/num/int_macros.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,8 @@ macro_rules! int_module {
369369
assert_eq_const_safe!(<$T>::midpoint(3, 4), 3);
370370
assert_eq_const_safe!(<$T>::midpoint(4, 3), 3);
371371

372-
assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), -1);
373-
assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), -1);
372+
assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), 0);
373+
assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), 0);
374374
assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN);
375375
assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX);
376376

0 commit comments

Comments
 (0)