Skip to content

Tidy up bigint multiplication methods#132195

Merged
bors merged 1 commit intorust-lang:masterfrom
clarfonthey:bigint-mul
Dec 31, 2024
Merged

Tidy up bigint multiplication methods#132195
bors merged 1 commit intorust-lang:masterfrom
clarfonthey:bigint-mul

Conversation

@clarfonthey
Copy link
Copy Markdown
Contributor

@clarfonthey clarfonthey commented Oct 26, 2024

This tidies up the library version of the bigint multiplication methods after the addition of the intrinsics in #133663. It follows this summary of what's desired for these methods.

Note that, if 2H = N, then uH::MAX * uH::MAX + uH::MAX + uH::MAX is uN::MAX, and that we can effectively add two "carry" values without overflowing.

For ease of terminology, the "low-order" or "least significant" or "wrapping" half of multiplication will be called the low part, and the "high-order" or "most significant" or "overflowing" half of multiplication will be called the high part. In all cases, the return convention is (low, high) and left unchanged by this PR, to be litigated later.

API Changes

The original API:

impl uN {
    // computes self * rhs
    pub const fn widening_mul(self, rhs: uN) -> (uN, uN);

    // computes self * rhs + carry
    pub const fn carrying_mul(self, rhs: uN, carry: uN) -> (uN, uN);
}

The added API:

impl uN {
    // computes self * rhs + carry1 + carry2
    pub const fn carrying2_mul(self, rhs: uN, carry: uN, add: uN) -> (uN, uN);
}
impl iN {
    // note that the low part is unsigned
    pub const fn widening_mul(self, rhs: iN) -> (uN, iN);
    pub const fn carrying_mul(self, rhs: iN, carry: iN) -> (uN, iN);
    pub const fn carrying_mul_add(self, rhs: iN, carry: iN, add: iN) -> (uN, iN);
}

Additionally, a naive implementation has been added for u128 and i128 since there are no double-wide types for those. Eventually, an intrinsic will be added to make these more efficient, but rather than doing this all at once, the library changes are added first.

Justifications for API

The unsigned parts are done to ensure consistency with overflowing addition: for a two's complement integer, you want to have unsigned overflow semantics for all parts of the integer except the highest one. This is because overflow for unsigned integers happens on the highest bit (from MAX to zero), whereas overflow for signed integers happens on the second highest bit (from MAX to MIN). Since the sign information only matters in the highest part, we use unsigned overflow for everything but that part.

There is still discussion on the merits of signed bigint addition methods, since getting the behaviour right is very subtle, but at least for signed bigint multiplication, the sign of the operands does make a difference. So, it feels appropriate that at least until we've nailed down the final API, there should be an option to do signed versions of these methods.

Additionally, while it's unclear whether we need all three versions of bigint multiplication (widening, carrying-1, and carrying-2), since it's possible to have up to two carries without overflow, there should at least be a method to allow that. We could potentially only offer the carry-2 method and expect that adding zero carries afterword will optimise correctly, but again, this can be litigated before stabilisation.

Note on documentation

While a lot of care was put into the documentation for the widening_mul and carrying_mul methods on unsigned integers, I have not taken this same care for carrying_mul_add or the signed versions. While I have updated the doc tests to be more appropriate, there will likely be many documentation changes done before stabilisation.

Note on tests

Alongside this change, I've added several tests to ensure that these methods work as expected. These are alongside the codegen tests for the intrinsics.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants