Skip to content

Commit 8fd49fe

Browse files
authored
Rollup merge of rust-lang#93873 - Stovent:big-ints, r=m-ou-se
Reimplement `carrying_add` and `borrowing_sub` for signed integers. As per the discussion in rust-lang#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.
2 parents 0568b0a + b998d82 commit 8fd49fe

File tree

4 files changed

+127
-0
lines changed

4 files changed

+127
-0
lines changed

library/core/src/num/int_macros.rs

+78
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,51 @@ macro_rules! int_impl {
15181518
(a as Self, b)
15191519
}
15201520

1521+
/// Calculates `self + rhs + carry` without the ability to overflow.
1522+
///
1523+
/// Performs "signed ternary addition" which takes in an extra bit to add, and may return an
1524+
/// additional bit of overflow. This signed function is used only on the highest-ordered data,
1525+
/// for which the signed overflow result indicates whether the big integer overflowed or not.
1526+
///
1527+
/// # Examples
1528+
///
1529+
/// Basic usage:
1530+
///
1531+
/// ```
1532+
/// #![feature(bigint_helper_methods)]
1533+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, false), (7, false));")]
1534+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, true), (8, false));")]
1535+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), (", stringify!($SelfT), "::MIN, true));")]
1536+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(0, true), (", stringify!($SelfT), "::MIN, true));")]
1537+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, true), (", stringify!($SelfT), "::MIN + 1, true));")]
1538+
#[doc = concat!("assert_eq!(",
1539+
stringify!($SelfT), "::MAX.carrying_add(", stringify!($SelfT), "::MAX, true), ",
1540+
"(-1, true));"
1541+
)]
1542+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.carrying_add(-1, true), (", stringify!($SelfT), "::MIN, false));")]
1543+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".carrying_add(", stringify!($SelfT), "::MAX, true), (", stringify!($SelfT), "::MIN, true));")]
1544+
/// ```
1545+
///
1546+
/// If `carry` is false, this method is equivalent to [`overflowing_add`](Self::overflowing_add):
1547+
///
1548+
/// ```
1549+
/// #![feature(bigint_helper_methods)]
1550+
#[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".carrying_add(2, false), 5_", stringify!($SelfT), ".overflowing_add(2));")]
1551+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), ", stringify!($SelfT), "::MAX.overflowing_add(1));")]
1552+
/// ```
1553+
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
1554+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
1555+
#[must_use = "this returns the result of the operation, \
1556+
without modifying the original"]
1557+
#[inline]
1558+
pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
1559+
// note: longer-term this should be done via an intrinsic.
1560+
// note: no intermediate overflow is required (https://github.com/rust-lang/rust/issues/85532#issuecomment-1032214946).
1561+
let (a, b) = self.overflowing_add(rhs);
1562+
let (c, d) = a.overflowing_add(carry as $SelfT);
1563+
(c, b != d)
1564+
}
1565+
15211566
/// Calculates `self` + `rhs` with an unsigned `rhs`
15221567
///
15231568
/// Returns a tuple of the addition along with a boolean indicating
@@ -1569,6 +1614,39 @@ macro_rules! int_impl {
15691614
(a as Self, b)
15701615
}
15711616

1617+
/// Calculates `self - rhs - borrow` without the ability to overflow.
1618+
///
1619+
/// Performs "signed ternary subtraction" which takes in an extra bit to subtract, and may return an
1620+
/// additional bit of overflow. This signed function is used only on the highest-ordered data,
1621+
/// for which the signed overflow result indicates whether the big integer overflowed or not.
1622+
///
1623+
/// # Examples
1624+
///
1625+
/// Basic usage:
1626+
///
1627+
/// ```
1628+
/// #![feature(bigint_helper_methods)]
1629+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, false), (3, false));")]
1630+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, true), (2, false));")]
1631+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, false), (-1, false));")]
1632+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, true), (-2, false));")]
1633+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.borrowing_sub(1, true), (", stringify!($SelfT), "::MAX - 1, true));")]
1634+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, false), (", stringify!($SelfT), "::MIN, true));")]
1635+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, true), (", stringify!($SelfT), "::MAX, false));")]
1636+
/// ```
1637+
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
1638+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
1639+
#[must_use = "this returns the result of the operation, \
1640+
without modifying the original"]
1641+
#[inline]
1642+
pub const fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) {
1643+
// note: longer-term this should be done via an intrinsic.
1644+
// note: no intermediate overflow is required (https://github.com/rust-lang/rust/issues/85532#issuecomment-1032214946).
1645+
let (a, b) = self.overflowing_sub(rhs);
1646+
let (c, d) = a.overflowing_sub(borrow as $SelfT);
1647+
(c, b != d)
1648+
}
1649+
15721650
/// Calculates `self` - `rhs` with an unsigned `rhs`
15731651
///
15741652
/// Returns a tuple of the subtraction along with a boolean indicating

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![feature(array_methods)]
44
#![feature(array_windows)]
55
#![feature(bench_black_box)]
6+
#![feature(bigint_helper_methods)]
67
#![feature(cell_update)]
78
#![feature(const_assume)]
89
#![feature(const_black_box)]

library/core/tests/num/int_macros.rs

+26
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,32 @@ macro_rules! int_module {
338338
assert_eq!(MIN.checked_next_multiple_of(-3), None);
339339
assert_eq!(MIN.checked_next_multiple_of(-1), Some(MIN));
340340
}
341+
342+
#[test]
343+
fn test_carrying_add() {
344+
assert_eq!($T::MAX.carrying_add(1, false), ($T::MIN, true));
345+
assert_eq!($T::MAX.carrying_add(0, true), ($T::MIN, true));
346+
assert_eq!($T::MAX.carrying_add(1, true), ($T::MIN + 1, true));
347+
assert_eq!($T::MAX.carrying_add(-1, false), ($T::MAX - 1, false));
348+
assert_eq!($T::MAX.carrying_add(-1, true), ($T::MAX, false)); // no intermediate overflow
349+
assert_eq!($T::MIN.carrying_add(-1, false), ($T::MAX, true));
350+
assert_eq!($T::MIN.carrying_add(-1, true), ($T::MIN, false)); // no intermediate overflow
351+
assert_eq!((0 as $T).carrying_add($T::MAX, true), ($T::MIN, true));
352+
assert_eq!((0 as $T).carrying_add($T::MIN, true), ($T::MIN + 1, false));
353+
}
354+
355+
#[test]
356+
fn test_borrowing_sub() {
357+
assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true));
358+
assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true));
359+
assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true));
360+
assert_eq!($T::MIN.borrowing_sub(-1, false), ($T::MIN + 1, false));
361+
assert_eq!($T::MIN.borrowing_sub(-1, true), ($T::MIN, false)); // no intermediate overflow
362+
assert_eq!($T::MAX.borrowing_sub(-1, false), ($T::MIN, true));
363+
assert_eq!($T::MAX.borrowing_sub(-1, true), ($T::MAX, false)); // no intermediate overflow
364+
assert_eq!((0 as $T).borrowing_sub($T::MIN, false), ($T::MIN, true));
365+
assert_eq!((0 as $T).borrowing_sub($T::MIN, true), ($T::MAX, false));
366+
}
341367
}
342368
};
343369
}

library/core/tests/num/uint_macros.rs

+22
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,28 @@ macro_rules! uint_module {
230230
assert_eq!((1 as $T).checked_next_multiple_of(0), None);
231231
assert_eq!(MAX.checked_next_multiple_of(2), None);
232232
}
233+
234+
#[test]
235+
fn test_carrying_add() {
236+
assert_eq!($T::MAX.carrying_add(1, false), (0, true));
237+
assert_eq!($T::MAX.carrying_add(0, true), (0, true));
238+
assert_eq!($T::MAX.carrying_add(1, true), (1, true));
239+
240+
assert_eq!($T::MIN.carrying_add($T::MAX, false), ($T::MAX, false));
241+
assert_eq!($T::MIN.carrying_add(0, true), (1, false));
242+
assert_eq!($T::MIN.carrying_add($T::MAX, true), (0, true));
243+
}
244+
245+
#[test]
246+
fn test_borrowing_sub() {
247+
assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true));
248+
assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true));
249+
assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true));
250+
251+
assert_eq!($T::MAX.borrowing_sub($T::MAX, false), (0, false));
252+
assert_eq!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false));
253+
assert_eq!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true));
254+
}
233255
}
234256
};
235257
}

0 commit comments

Comments
 (0)