mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
Auto merge of #93873 - Stovent:big-ints, r=m-ou-se
Reimplement `carrying_add` and `borrowing_sub` for signed integers. As per the discussion in #85532, this PR reimplements `carrying_add` and `borrowing_sub` for signed integers. It also adds unit tests for both unsigned and signed integers, emphasing on the behaviours of the methods.
This commit is contained in:
commit
7200da0217
@ -1518,6 +1518,51 @@ macro_rules! int_impl {
|
||||
(a as Self, b)
|
||||
}
|
||||
|
||||
/// Calculates `self + rhs + carry` without the ability to overflow.
|
||||
///
|
||||
/// Performs "signed ternary addition" which takes in an extra bit to add, and may return an
|
||||
/// additional bit of overflow. This signed function is used only on the highest-ordered data,
|
||||
/// for which the signed overflow result indicates whether the big integer overflowed or not.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, false), (7, false));")]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, true), (8, false));")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), (", stringify!($SelfT), "::MIN, true));")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(0, true), (", stringify!($SelfT), "::MIN, true));")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, true), (", stringify!($SelfT), "::MIN + 1, true));")]
|
||||
#[doc = concat!("assert_eq!(",
|
||||
stringify!($SelfT), "::MAX.carrying_add(", stringify!($SelfT), "::MAX, true), ",
|
||||
"(-1, true));"
|
||||
)]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.carrying_add(-1, true), (", stringify!($SelfT), "::MIN, false));")]
|
||||
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".carrying_add(", stringify!($SelfT), "::MAX, true), (", stringify!($SelfT), "::MIN, true));")]
|
||||
/// ```
|
||||
///
|
||||
/// If `carry` is false, this method is equivalent to [`overflowing_add`](Self::overflowing_add):
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
#[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".carrying_add(2, false), 5_", stringify!($SelfT), ".overflowing_add(2));")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), ", stringify!($SelfT), "::MAX.overflowing_add(1));")]
|
||||
/// ```
|
||||
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
|
||||
// note: longer-term this should be done via an intrinsic.
|
||||
// note: no intermediate overflow is required (https://github.com/rust-lang/rust/issues/85532#issuecomment-1032214946).
|
||||
let (a, b) = self.overflowing_add(rhs);
|
||||
let (c, d) = a.overflowing_add(carry as $SelfT);
|
||||
(c, b != d)
|
||||
}
|
||||
|
||||
/// Calculates `self` + `rhs` with an unsigned `rhs`
|
||||
///
|
||||
/// Returns a tuple of the addition along with a boolean indicating
|
||||
@ -1569,6 +1614,39 @@ macro_rules! int_impl {
|
||||
(a as Self, b)
|
||||
}
|
||||
|
||||
/// Calculates `self - rhs - borrow` without the ability to overflow.
|
||||
///
|
||||
/// Performs "signed ternary subtraction" which takes in an extra bit to subtract, and may return an
|
||||
/// additional bit of overflow. This signed function is used only on the highest-ordered data,
|
||||
/// for which the signed overflow result indicates whether the big integer overflowed or not.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, false), (3, false));")]
|
||||
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, true), (2, false));")]
|
||||
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, false), (-1, false));")]
|
||||
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, true), (-2, false));")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.borrowing_sub(1, true), (", stringify!($SelfT), "::MAX - 1, true));")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, false), (", stringify!($SelfT), "::MIN, true));")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, true), (", stringify!($SelfT), "::MAX, false));")]
|
||||
/// ```
|
||||
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) {
|
||||
// note: longer-term this should be done via an intrinsic.
|
||||
// note: no intermediate overflow is required (https://github.com/rust-lang/rust/issues/85532#issuecomment-1032214946).
|
||||
let (a, b) = self.overflowing_sub(rhs);
|
||||
let (c, d) = a.overflowing_sub(borrow as $SelfT);
|
||||
(c, b != d)
|
||||
}
|
||||
|
||||
/// Calculates `self` - `rhs` with an unsigned `rhs`
|
||||
///
|
||||
/// Returns a tuple of the subtraction along with a boolean indicating
|
||||
|
@ -3,6 +3,7 @@
|
||||
#![feature(array_methods)]
|
||||
#![feature(array_windows)]
|
||||
#![feature(bench_black_box)]
|
||||
#![feature(bigint_helper_methods)]
|
||||
#![feature(cell_update)]
|
||||
#![feature(const_assume)]
|
||||
#![feature(const_black_box)]
|
||||
|
@ -338,6 +338,32 @@ macro_rules! int_module {
|
||||
assert_eq!(MIN.checked_next_multiple_of(-3), None);
|
||||
assert_eq!(MIN.checked_next_multiple_of(-1), Some(MIN));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_carrying_add() {
|
||||
assert_eq!($T::MAX.carrying_add(1, false), ($T::MIN, true));
|
||||
assert_eq!($T::MAX.carrying_add(0, true), ($T::MIN, true));
|
||||
assert_eq!($T::MAX.carrying_add(1, true), ($T::MIN + 1, true));
|
||||
assert_eq!($T::MAX.carrying_add(-1, false), ($T::MAX - 1, false));
|
||||
assert_eq!($T::MAX.carrying_add(-1, true), ($T::MAX, false)); // no intermediate overflow
|
||||
assert_eq!($T::MIN.carrying_add(-1, false), ($T::MAX, true));
|
||||
assert_eq!($T::MIN.carrying_add(-1, true), ($T::MIN, false)); // no intermediate overflow
|
||||
assert_eq!((0 as $T).carrying_add($T::MAX, true), ($T::MIN, true));
|
||||
assert_eq!((0 as $T).carrying_add($T::MIN, true), ($T::MIN + 1, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrowing_sub() {
|
||||
assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true));
|
||||
assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true));
|
||||
assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true));
|
||||
assert_eq!($T::MIN.borrowing_sub(-1, false), ($T::MIN + 1, false));
|
||||
assert_eq!($T::MIN.borrowing_sub(-1, true), ($T::MIN, false)); // no intermediate overflow
|
||||
assert_eq!($T::MAX.borrowing_sub(-1, false), ($T::MIN, true));
|
||||
assert_eq!($T::MAX.borrowing_sub(-1, true), ($T::MAX, false)); // no intermediate overflow
|
||||
assert_eq!((0 as $T).borrowing_sub($T::MIN, false), ($T::MIN, true));
|
||||
assert_eq!((0 as $T).borrowing_sub($T::MIN, true), ($T::MAX, false));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -230,6 +230,28 @@ macro_rules! uint_module {
|
||||
assert_eq!((1 as $T).checked_next_multiple_of(0), None);
|
||||
assert_eq!(MAX.checked_next_multiple_of(2), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_carrying_add() {
|
||||
assert_eq!($T::MAX.carrying_add(1, false), (0, true));
|
||||
assert_eq!($T::MAX.carrying_add(0, true), (0, true));
|
||||
assert_eq!($T::MAX.carrying_add(1, true), (1, true));
|
||||
|
||||
assert_eq!($T::MIN.carrying_add($T::MAX, false), ($T::MAX, false));
|
||||
assert_eq!($T::MIN.carrying_add(0, true), (1, false));
|
||||
assert_eq!($T::MIN.carrying_add($T::MAX, true), (0, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrowing_sub() {
|
||||
assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true));
|
||||
assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true));
|
||||
assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true));
|
||||
|
||||
assert_eq!($T::MAX.borrowing_sub($T::MAX, false), (0, false));
|
||||
assert_eq!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false));
|
||||
assert_eq!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user