Change:
I had this question on the currently unreleased ; on signed carry (probably applies to substraction too ?); I have this test failing with the current code: Playground: Is the test correct ? A carry did happen in an intermediate calculation, but should the overall calculation report a carry ?
I agree that this is a bug. There's a carry when the calculation is done as unsigned, but this is not the same in signed. Another example: That incorrectly returns .
There's some discussion about that in the tracking issue, starting with
Yeah, it looks like I rehashed the same arguments, that's what I get for reading comments and doc too fast. Still, I think that this is surprising enough that it would be nice to address it somehow.
I agree this is a bug. It looks like it's implementing the signed version using the unsigned one, returning the same carry flag (), but that's incorrect. Here's another demonstration that it needs an update: you be up to making a fix PR, You can use to get assigned to this issue.
I have no bandwidth for making a fix right now. Since this was supposed to be helpers for bigint operations, my first idea was to just remove the signed variants. I have no idea if this would be appropriate. Anyone is welcome to do a proper fix, it shouldn't be too complicated.
A thought: What should do? It feels to me like the carryout from that should perhaps be , since the carry from that is different from the one in , which would imply that it couldn't be anymore. We do have a type that's -1, 0, or 1: . I can't decide if that's clever or horrible. Or should we just delete the signed versions for now?
I think should return , because signed and unsigned operations should give the same results, so should be computed as which gives , then truncated to 8-bits which gives . Then because gives , which can't be represented as an 8-bits signed number, then there is overflow to me. Now for the -1, 0, 1 overflow, I don't think it's necessary because the point of the bit is to indicate if it happened or not. The direction of overflow is indicated by the resulting value. If I am correct, then this raises another question: should intermediate overflow be detected or not ? for example: gives a result of -128, so the output is the same as the input so no overflow. But if we break down the operations, overflows to 127, and then overflows back to -128. In this case should an overflow occur or not ? Considering my previous example, I personally think it intermediate overflow shouldn't be detected because it would be misleading: it would indicate that the sign of the source operands and the result are different while they are the same. Tho I'd like other's opinion.
I think so. The unsigned versions have clear semantics, but it gets weird with signed values, and as a "bigint helper" I'd be surprised to see one that uses signed digits in the first place.

Discussion:
(a as Self, b) } <del> /// 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. /// /// # 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, false));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, true), (", stringify!($SelfT), "::MIN + 1, 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 carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) { let (sum, carry) = (self as $UnsignedT).carrying_add(rhs as $UnsignedT, carry); (sum as $SelfT, carry) } </del> /// Calculates `self` + `rhs` with an unsigned `rhs` /// /// Returns a tuple of the addition along with a boolean indicating

(a as Self, b) } <del> /// 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!(", stringify!($SelfT), "::MIN.borrowing_sub(1, false), (", stringify!($SelfT), "::MAX, false));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.borrowing_sub(1, true), (", stringify!($SelfT), "::MAX - 1, 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) { let (sum, borrow) = (self as $UnsignedT).borrowing_sub(rhs as $UnsignedT, borrow); (sum as $SelfT, borrow) } </del> /// Calculates `self` - `rhs` with an unsigned `rhs` /// /// Returns a tuple of the subtraction along with a boolean indicating

macro_rules! widening_impl { ($SelfT:ty, $WideT:ty, $BITS:literal, unsigned) => { <del> widening_impl!($SelfT, $WideT, $BITS, ""); }; ($SelfT:ty, $WideT:ty, $BITS:literal, signed) => { widening_impl!($SelfT, $WideT, $BITS, "# //"); }; ($SelfT:ty, $WideT:ty, $BITS:literal, $AdaptiveTestPrefix:literal) => { </del> /// Calculates the complete product `self * rhs` without the possibility to overflow. /// /// This returns the low-order (wrapping) bits and the high-order (overflow) bits

/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0)); /// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2)); /// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2)); <del> #[doc = concat!($AdaptiveTestPrefix, "assert_eq!(", </del> <ins> #[doc = concat!("assert_eq!(", </ins> stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", "(0, ", stringify!($SelfT), "::MAX));" )]

impl i8 { int_impl! { i8, i8, u8, 8, 7, -128, 127, 2, "-0x7e", "0xa", "0x12", "0x12", "0x48", "[0x12]", "[0x12]", "", "" } <del> widening_impl! { i8, i16, 8, signed } </del> } #[lang = "i16"] impl i16 { int_impl! { i16, i16, u16, 16, 15, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } <del> widening_impl! { i16, i32, 16, signed } </del> } #[lang = "i32"]

int_impl! { i32, i32, u32, 32, 31, -2147483648, 2147483647, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } <del> widening_impl! { i32, i64, 32, signed } </del> } #[lang = "i64"]

"0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", "", "" } <del> widening_impl! { i64, i128, 64, signed } </del> } #[lang = "i128"]

int_impl! { isize, i16, usize, 16, 15, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } <del> widening_impl! { isize, i32, 16, signed } </del> } #[cfg(target_pointer_width = "32")]

"0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } <del> widening_impl! { isize, i64, 32, signed } </del> } #[cfg(target_pointer_width = "64")]

"0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } <del> widening_impl! { isize, i128, 64, signed } </del> } /// If 6th bit set ascii is upper case.