clarfonthey · GitHub

Feature gate: #![feature(bigint_helper_methods)]

Stable (1.91.0)

impl uN {
    fn carrying_add(self, rhs: uN, carry: bool) -> (uN, bool);
    fn borrowing_sub(self, rhs: uN, borrow: bool) -> (uN, bool);
    fn carrying_mul(self, rhs: uN, carry: uN) -> (uN, uN);
}

Const-unstable

impl uN {
    const fn carrying_add(self, rhs: uN, carry: bool) -> (uN, bool);
    const fn borrowing_sub(self, rhs: uN, borrow: bool) -> (uN, bool);
    const fn carrying_mul(self, rhs: uN, carry: uN) -> (uN, uN);
    const fn carrying_mul_add(self, rhs: uN, carry: uN, add: uN) -> (uN, uN);
}

Unstable

impl uN {
    const fn widening_mul(self, rhs: uN) -> (uN, uN);
}
impl iN {
    const fn widening_mul(self, rhs: uN) -> (iN, iN);
}

The below methods are now tracked in #151989:

impl iN {
    const fn carrying_add(self, rhs: iN, carry: bool) -> (iN, bool);
    const fn borrowing_sub(self, rhs: iN, carry: bool) -> (iN, bool);
    const fn carrying_mul(self, rhs: iN, carry: iN) -> (uN, iN);
    const fn carrying_mul_unsigned(self, rhs: uN, carry: uN) -> (uN, iN);
    const fn carrying_mul_add(self, rhs: iN, carry: iN, add: iN) -> (uN, iN);
    const fn carrying_mul_unsigned_add(self, rhs: uN, carry: uN, add: iN) -> (uN, iN);
}

Previous Description

These methods are intended to help centralise the effort required for creating efficient big integer implementations, by offering a few methods which would otherwise require special compiler intrinsics or custom assembly code in order to do efficiently. They do not alone constitute big integer implementations themselves, but are necessary building blocks for a larger implementation.

Public API

// On unsigned integers:
/// `self * rhs` (wide multiplication, same as `self.carrying_mul(rhs, 0)`)
const fn widening_mul(self, rhs: Self) -> (Self, Self);
// On signed integers:
/// `self + rhs + carry` (full adder)
const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool);
/// `self - rhs - carry` (full "subtractor")
const fn borrowing_sub(self, rhs: Self, carry: bool) -> (Self, bool);

Stabilized as part of 1.91, but not yet const stable:

// On unsigned integers:
/// `self + rhs + carry` (full adder)
const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool);
/// `self - rhs - carry` (full "subtractor")
const fn borrowing_sub(self, rhs: Self, carry: bool) -> (Self, bool);
/// `self * rhs + carry` (multiply-accumulate)
const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self);
/// `self * rhs + carry` (multiply-accumulate-carry)
const fn carrying_mul_add(self, rhs: Self, addend: Self, carry: Self) -> (Self, Self);

Steps / History

Unresolved Questions

  • Should these be implemented using compiler intrinsics? LLVM currently has no equivalents, so, we'd have to custom-build some.
  • Should an alternative API be provided for widening_mul that simply returns the next-larger type? What would we do for u128/i128?
  • What should the behaviour be for signed integers? Should there be implementations for signed integers at all?
  • Is the "borrowing" terminology worth it for subtraction, or should we simply call that "carrying" as well for consistency?
  • Are there other methods that should be added in addition to the existing ones?

Read the original on github.com ↗