Tracking Issue for bigint helper methods #85532
Copy link
Copy link
Closed
Labels
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
Description
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
-
widening_mulRFC: widening_mul rfcs#2417 - Initial implementation of
carrying_add,borrowing_sub,carrying_mul, andwidening_mulAdd carrying_add, borrowing_sub, widening_mul, carrying_mul methods to integers #85017 - Remove implementations on signed types Remove bigint_helper_methods for *signed* types #90848 per discussion in Signed carry carrying_add in bigint_helper_methods #90541
- Add new
carrying_addandborrowing_subon signed types: Reimplementcarrying_addandborrowing_subfor signed integers. #93873 - Add a compiler intrinsic to back
bigint_helper_methods#133663 - Add assembly tests to ensure this keeps doing the right thing
- One for
carrying_addwas added in Fix chainingcarrying_adds #133674
- One for
- Clarify documentation
("without the ability to overflow" can be confusing.) - Final commenting period (FCP)
- Partial Stabilization PR Partial-stabilize the basics from
bigint_helper_methods#144494
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_multhat simply returns the next-larger type? What would we do foru128/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?
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
Activity
leonardo-m commentedon May 21, 2021
I'd like a mul_mod, as shown in #85017, because I think you can't implement it efficiently without asm and it's a basic block for power_mod and other things.
clarfonthey commentedon May 21, 2021
Another set of methods that could be useful that I'll probably offer implementations for at some point:
Essentially, return the two halves of a rotation, i.e.
x.widening_shl(y)is the same as(x << y, x >> (BITS - y))and similarly forwidening_shr. Not sure whether they should allowrhs == BITSor not, but presumably they wouldn't for consistency with existing shift methods.clarfonthey commentedon Jun 1, 2021
From @scottmcm in the original PR:
photino commentedon Sep 7, 2021
Why don't we add
carrying_mulandwidening_mulfori128/u128as well?clarfonthey commentedon Sep 7, 2021
Mostly effort implementing them efficiently. In the meantime, you can do it with four calls to the
u64version. Or three if you want to be fancy.RalfJung commentedon Sep 9, 2021
I was very confused by this function name at first, since borrowing in Rust usually refers to references. I am not a native speaker, but I do formal mathematical work in English professionally, and yet I never before heard the term "borrowing" in the context of subtraction. So I think this, at least, needs some explanation in the docs. (I would have expected something like
carrying_sub, but maybe that is nonsense for a native speaker.)The current docs for some of the other methods could probably also be improved: they talk about not having the "ability to overflow", which makes it sound like not overflowing is a bad thing.
AaronKutch commentedon Sep 9, 2021
The word borrow here comes from the terminology for a full subtractor. I am thinking that maybe the borrowing_sub function could be removed altogether. The same effect that borrowing_sub has can be obtained from carrying_add by making the first carrying_add in the chain have a set carry bit, and then bitnot every rhs. This fact could be put in the documentation of carrying_add.
clarfonthey commentedon Sep 9, 2021
Considering how the primary goal of these methods is to be as efficient as possible, usually optimising down to a single instruction, I don't think it'd be reasonable to just get rid of subtraction in favour of telling everyone to use addition instead. Definitely open to changing the name, though.
AaronKutch commentedon Sep 9, 2021
These helper methods will not be very useful to me unless they are implemented for every kind of integer. Here is an implementation for a widening multiplication-addition for u128:
I have tested this with my crate
awint.edit: There is a version of this that uses the Karatsuba trick to use 3 multiplications instead of 4, but it incurs extra summations, branches, and is not as parallel. For typical desktop processors the above should be the fastest.
clarfonthey commentedon Sep 9, 2021
I would make a PR for that.
AaronKutch commentedon Sep 9, 2021
Some alternative signatures include
u128::widen_mul_add(lhs, rhs, add),lhs.widen_mul_add(rhs, add), oradd.widen_mul_add(lhs, rhs). Inawintmy general purpose mul-add function ismul_add_triopwhich uses the third signature but takesselfmutably and add-assignslhs * rhs. I'm not sure which is best.AaronKutch commentedon Sep 9, 2021
I would also change up the documentation headers for the
carrying_addfunction to sayI specifically note
unsigned overflow, because that happens for both signed and unsignedintegers because of how two's complement works.
AaronKutch commentedon Sep 9, 2021
borrowing_subshould be left in with its naming, but its documentation could be128 remaining items
Auto merge of #144494 - scottmcm:min_bigint_helpers, r=Mark-Simulacrum
tarcieri commentedon Oct 30, 2025
It looks like all of these (for
u{N}) exceptwidening_mulshipped in Rust 1.91: https://blog.rust-lang.org/2025/10/30/Rust-1.91.0/ (stabilization PR)I would be curious where to track
const fnsupport as that's the one piece missing for our purposestgross35 commentedon Oct 30, 2025
Updated the issue
From the stabilization PR #144494:
I think anyone interested could probably make a PR to const stabilize these. It was a good idea to keep them separate but I don't think there is anything that would prevent const stabilization now, given const traits are used for convenience rather than out of necessity.
Cc @scottmcm in case you were planning to follow up here yourself
clarfonthey commentedon Oct 30, 2025
This is just my personal 2¢, but I think that the signed integer methods and the widening multiplication should be separated out into two new issues, keeping this issue only for the const-stabilisation of the existing methods. Maybe even creating a new thread for that and closing this one.
This thread has become a bit of a mess referencing a lot of things and I think it would be helpful to separate them out, even if the feature flags remain unified for now.
I personally would expect any PRs preparing these sets of methods, updates regarding their design, and stabilisation FCPs will be separate.
Auto merge of rust-lang#144494 - scottmcm:min_bigint_helpers, r=Mark-…
Auto merge of #144494 - scottmcm:min_bigint_helpers, r=Mark-Simulacrum
nickkuk commentedon Feb 2, 2026
I opened new discussion for signed integer methods: #151989.
clarfonthey commentedon Feb 2, 2026
Place for discussing const-stability of currently stable methods: #152015
clarfonthey commentedon Feb 2, 2026
Widening multiplication: #152016
clarfonthey commentedon Feb 2, 2026
Since that should cover all the unstable methods with the feature flag, I'm going to make a PR to move the tracking issues over and then close this one. As mentioned, a lot of discussion has happened and with part of the feature already merged, it's a bit confusing to track here; we should move into the three new issues for discussion instead.
Rollup merge of rust-lang#152018 - clarfonthey:bigint-tracking-issues…
Unrolled build for #152018
Rollup merge of #152018 - clarfonthey:bigint-tracking-issues, r=scottmcm
fix(amm): U256::mul four-product carry propagation