From 12660997425e3d2c9ace188531eff02e52ad7262 Mon Sep 17 00:00:00 2001 From: Stovent Date: Thu, 10 Feb 2022 00:24:12 -0500 Subject: [PATCH 1/3] Implement carrying_add and borrowing_sub on signed numbers --- library/core/src/num/int_macros.rs | 80 +++++++++++++++++++++++++++ library/core/tests/lib.rs | 1 + library/core/tests/num/int_macros.rs | 26 +++++++++ library/core/tests/num/uint_macros.rs | 22 ++++++++ 4 files changed, 129 insertions(+) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 1f435784be14c..4e51b90b835cf 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1512,6 +1512,53 @@ macro_rules! int_impl { (a as Self, b) } + /// Calculates `self + rhs + carry` without the ability to overflow. + /// + /// Performs "ternary addition" which takes in an extra bit to add, and may return an + /// additional bit of overflow. This allows for chaining together multiple additions + /// to create "big integers" which represent larger values. + /// + #[doc = concat!("This can be thought of as a ", stringify!($BITS), "-bit \"full adder\", in the electronics sense.")] + /// + /// # 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 @@ -1563,6 +1610,39 @@ macro_rules! int_impl { (a as Self, b) } + /// Calculates `self - rhs - borrow` without the ability to overflow. + /// + /// Performs "ternary subtraction" which takes in an extra bit to subtract, and may return + /// an additional bit of overflow. This allows for chaining together multiple subtractions + /// to create "big integers" which represent larger values. + /// + /// # 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 diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 7e9d7d2710180..3132d8adb3620 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -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)] diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs index 8b84a78e6be08..18c55e43aac81 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/core/tests/num/int_macros.rs @@ -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)); + } } }; } diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs index 93ae620c23302..15ae9f2324f6c 100644 --- a/library/core/tests/num/uint_macros.rs +++ b/library/core/tests/num/uint_macros.rs @@ -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)); + } } }; } From 5c690555f226909786a43b4bf0dc58f8bffa7a6b Mon Sep 17 00:00:00 2001 From: Stovent Date: Fri, 11 Feb 2022 15:25:51 -0500 Subject: [PATCH 2/3] Correct signed bit int documentation --- library/core/src/num/int_macros.rs | 74 ++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 4e51b90b835cf..0cd64753f9b2e 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1514,15 +1514,41 @@ macro_rules! int_impl { /// Calculates `self + rhs + carry` without the ability to overflow. /// - /// Performs "ternary addition" which takes in an extra bit to add, and may return an - /// additional bit of overflow. This allows for chaining together multiple additions - /// to create "big integers" which represent larger values. - /// - #[doc = concat!("This can be thought of as a ", stringify!($BITS), "-bit \"full adder\", in the electronics sense.")] + /// 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 + /// Standard signed bit integer implementation + /// + /// ```rs + /// #![feature(bigint_helper_methods)] + /// struct I16 { + /// pub low: u8, // Low-order bytes has to be unsigned. + /// /// Most Significant Data has to be of the same signedness as the desired type. + /// /// So u8 to implement U16, i8 to implement I16. + /// pub high: i8, + /// } + /// + /// impl I16 { + /// /// Adds `rhs` to `self` and returns true if signed overflow occurs, false otherwise. + /// pub fn overflowing_add(&mut self, rhs: Self) -> bool { + /// let (low_res, low_carry) = self.low.carrying_add(rhs.low, false); + /// + /// // The signed `carrying_add` method is used to detect signed overflow. + /// let (high_res, high_carry) = self.high.carrying_add(rhs.high, low_carry); + /// + /// self.low = low_res; + /// self.high = high_res; + /// high_carry + /// } + /// } + /// + /// fn main() {} + /// ``` + /// + /// General behavior /// /// ``` /// #![feature(bigint_helper_methods)] @@ -1612,13 +1638,41 @@ macro_rules! int_impl { /// Calculates `self - rhs - borrow` without the ability to overflow. /// - /// Performs "ternary subtraction" which takes in an extra bit to subtract, and may return - /// an additional bit of overflow. This allows for chaining together multiple subtractions - /// to create "big integers" which represent larger values. + /// 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 + /// Standard signed bit integer implementation + /// + /// ```rs + /// #![feature(bigint_helper_methods)] + /// struct I16 { + /// pub low: u8, // Low-order bytes has to be unsigned. + /// /// Most Significant Data has to be of the same signedness as the desired type. + /// /// So u8 to implement U16, i8 to implement I16. + /// pub high: i8, + /// } + /// + /// impl I16 { + /// /// Subtracts `rhs` from `self` and returns true if signed overflow occurs, false otherwise. + /// pub fn overflowing_sub(&mut self, rhs: Self) -> bool { + /// let (low_res, low_carry) = self.low.borrowing_sub(rhs.low, false); + /// + /// // The signed `borrowing_sub` method is used to detect signed overflow. + /// let (high_res, high_carry) = self.high.borrowing_sub(rhs.high, low_carry); + /// + /// self.low = low_res; + /// self.high = high_res; + /// high_carry + /// } + /// } + /// + /// fn main() {} + /// ``` + /// + /// General behavior /// /// ``` /// #![feature(bigint_helper_methods)] From b998d82d8d098ee0e5127123543f5720965c6648 Mon Sep 17 00:00:00 2001 From: Stovent Date: Fri, 11 Feb 2022 15:47:37 -0500 Subject: [PATCH 3/3] Remove too long example --- library/core/src/num/int_macros.rs | 60 +----------------------------- 1 file changed, 2 insertions(+), 58 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 0cd64753f9b2e..2d97fa62accd6 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1520,35 +1520,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Standard signed bit integer implementation - /// - /// ```rs - /// #![feature(bigint_helper_methods)] - /// struct I16 { - /// pub low: u8, // Low-order bytes has to be unsigned. - /// /// Most Significant Data has to be of the same signedness as the desired type. - /// /// So u8 to implement U16, i8 to implement I16. - /// pub high: i8, - /// } - /// - /// impl I16 { - /// /// Adds `rhs` to `self` and returns true if signed overflow occurs, false otherwise. - /// pub fn overflowing_add(&mut self, rhs: Self) -> bool { - /// let (low_res, low_carry) = self.low.carrying_add(rhs.low, false); - /// - /// // The signed `carrying_add` method is used to detect signed overflow. - /// let (high_res, high_carry) = self.high.carrying_add(rhs.high, low_carry); - /// - /// self.low = low_res; - /// self.high = high_res; - /// high_carry - /// } - /// } - /// - /// fn main() {} - /// ``` - /// - /// General behavior + /// Basic usage: /// /// ``` /// #![feature(bigint_helper_methods)] @@ -1644,35 +1616,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Standard signed bit integer implementation - /// - /// ```rs - /// #![feature(bigint_helper_methods)] - /// struct I16 { - /// pub low: u8, // Low-order bytes has to be unsigned. - /// /// Most Significant Data has to be of the same signedness as the desired type. - /// /// So u8 to implement U16, i8 to implement I16. - /// pub high: i8, - /// } - /// - /// impl I16 { - /// /// Subtracts `rhs` from `self` and returns true if signed overflow occurs, false otherwise. - /// pub fn overflowing_sub(&mut self, rhs: Self) -> bool { - /// let (low_res, low_carry) = self.low.borrowing_sub(rhs.low, false); - /// - /// // The signed `borrowing_sub` method is used to detect signed overflow. - /// let (high_res, high_carry) = self.high.borrowing_sub(rhs.high, low_carry); - /// - /// self.low = low_res; - /// self.high = high_res; - /// high_carry - /// } - /// } - /// - /// fn main() {} - /// ``` - /// - /// General behavior + /// Basic usage: /// /// ``` /// #![feature(bigint_helper_methods)]