Reimplement carrying_add and borrowing_sub for signed integers. by Stovent · Pull Request #93873 · rust-lang/rust (original) (raw)

I have been a little busy these past weeks, sorry for late answer.

First, I made the following example that uses both the signed and unsigned versions of the carrying / borrowing methods.

#![feature(bigint_helper_methods)] #![feature(array_zip)]

#[derive(Clone, Copy)] struct SignedBigInt { pub lows: [u8; N], pub high: i8, }

impl SignedBigInt { /// Subtracts rhs from self and returns true if signed overflow occurs, false otherwise. pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) { let mut borrow = false;

    let lows = self.lows.zip(rhs.lows).map(|(left, right)| {
        let (res, b) = left.borrowing_sub(right, borrow);
        borrow = b;
        res
    });

    let (high, b) = self.high.borrowing_sub(rhs.high, borrow);

    (Self { lows, high }, b)
}

}

fn main() { let left = SignedBigInt { // -32_768 lows: [0], high: -128, }; let right = SignedBigInt { // 127 lows: [127], high: 0, }; let (res, borrow) = left.overflowing_sub(right); // -32_895 overflow to 32_641

assert_eq!(res.high, 0x7F);
assert_eq!(res.lows[0], 0x81);
assert_eq!(borrow, true);

}

From my experience, the signed methods are only useful for fixed-size big integers, since dynamically-sized big ints operations do not have overflow as we simply allocate the size needed for the result. If you need further examples, please tell me which points I missed in those I sent.

Second, regarding a potential name change. It is true that the signed methods are often used differently than the unsigned ones. However, since their behaviour is similar, just using signed semantics instead of unsigned ones, I am not sure whether a name change is really justified. If you still want to rename these methods, I personally cannot think of better names than the existing ones, but I am open for suggestions.