Skip to content

Commit 1266099

Browse files
committed
Implement carrying_add and borrowing_sub on signed numbers
1 parent 4a8d2e3 commit 1266099

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

library/core/src/num/int_macros.rs

+80
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,53 @@ macro_rules! int_impl {
15121512
(a as Self, b)
15131513
}
15141514

1515+
/// Calculates `self + rhs + carry` without the ability to overflow.
1516+
///
1517+
/// Performs "ternary addition" which takes in an extra bit to add, and may return an
1518+
/// additional bit of overflow. This allows for chaining together multiple additions
1519+
/// to create "big integers" which represent larger values.
1520+
///
1521+
#[doc = concat!("This can be thought of as a ", stringify!($BITS), "-bit \"full adder\", in the electronics sense.")]
1522+
///
1523+
/// # Examples
1524+
///
1525+
/// Basic usage
1526+
///
1527+
/// ```
1528+
/// #![feature(bigint_helper_methods)]
1529+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, false), (7, false));")]
1530+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, true), (8, false));")]
1531+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), (", stringify!($SelfT), "::MIN, true));")]
1532+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(0, true), (", stringify!($SelfT), "::MIN, true));")]
1533+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, true), (", stringify!($SelfT), "::MIN + 1, true));")]
1534+
#[doc = concat!("assert_eq!(",
1535+
stringify!($SelfT), "::MAX.carrying_add(", stringify!($SelfT), "::MAX, true), ",
1536+
"(-1, true));"
1537+
)]
1538+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.carrying_add(-1, true), (", stringify!($SelfT), "::MIN, false));")]
1539+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".carrying_add(", stringify!($SelfT), "::MAX, true), (", stringify!($SelfT), "::MIN, true));")]
1540+
/// ```
1541+
///
1542+
/// If `carry` is false, this method is equivalent to [`overflowing_add`](Self::overflowing_add):
1543+
///
1544+
/// ```
1545+
/// #![feature(bigint_helper_methods)]
1546+
#[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".carrying_add(2, false), 5_", stringify!($SelfT), ".overflowing_add(2));")]
1547+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), ", stringify!($SelfT), "::MAX.overflowing_add(1));")]
1548+
/// ```
1549+
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
1550+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
1551+
#[must_use = "this returns the result of the operation, \
1552+
without modifying the original"]
1553+
#[inline]
1554+
pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
1555+
// note: longer-term this should be done via an intrinsic.
1556+
// note: no intermediate overflow is required (https://github.com/rust-lang/rust/issues/85532#issuecomment-1032214946).
1557+
let (a, b) = self.overflowing_add(rhs);
1558+
let (c, d) = a.overflowing_add(carry as $SelfT);
1559+
(c, b != d)
1560+
}
1561+
15151562
/// Calculates `self` + `rhs` with an unsigned `rhs`
15161563
///
15171564
/// Returns a tuple of the addition along with a boolean indicating
@@ -1563,6 +1610,39 @@ macro_rules! int_impl {
15631610
(a as Self, b)
15641611
}
15651612

1613+
/// Calculates `self - rhs - borrow` without the ability to overflow.
1614+
///
1615+
/// Performs "ternary subtraction" which takes in an extra bit to subtract, and may return
1616+
/// an additional bit of overflow. This allows for chaining together multiple subtractions
1617+
/// to create "big integers" which represent larger values.
1618+
///
1619+
/// # Examples
1620+
///
1621+
/// Basic usage
1622+
///
1623+
/// ```
1624+
/// #![feature(bigint_helper_methods)]
1625+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, false), (3, false));")]
1626+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, true), (2, false));")]
1627+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, false), (-1, false));")]
1628+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, true), (-2, false));")]
1629+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.borrowing_sub(1, true), (", stringify!($SelfT), "::MAX - 1, true));")]
1630+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, false), (", stringify!($SelfT), "::MIN, true));")]
1631+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, true), (", stringify!($SelfT), "::MAX, false));")]
1632+
/// ```
1633+
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
1634+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
1635+
#[must_use = "this returns the result of the operation, \
1636+
without modifying the original"]
1637+
#[inline]
1638+
pub const fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) {
1639+
// note: longer-term this should be done via an intrinsic.
1640+
// note: no intermediate overflow is required (https://github.com/rust-lang/rust/issues/85532#issuecomment-1032214946).
1641+
let (a, b) = self.overflowing_sub(rhs);
1642+
let (c, d) = a.overflowing_sub(borrow as $SelfT);
1643+
(c, b != d)
1644+
}
1645+
15661646
/// Calculates `self` - `rhs` with an unsigned `rhs`
15671647
///
15681648
/// 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)