Skip to content

Commit b53c72f

Browse files
committed
Auto merge of #144494 - scottmcm:min_bigint_helpers, r=Mark-Simulacrum
Partial-stabilize the basics from `bigint_helper_methods` Direct link to p-FCP comment: #144494 (comment) After libs-api discussion, this is now the following methods: - [`uN::carrying_add`](https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.carrying_add): uN + uN + bool -> (uN, bool) - [`uN::borrowing_sub`](https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.borrowing_sub): uN + uN + bool -> (uN, bool) - [`uN::carrying_mul`](https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.carrying_mul): uN * uN + uN -> (uN, uN) - [`uN::carrying_mul_add`](https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.carrying_mul_add): uN * uN + uN + uN -> (uN, uN) Specifically, these are the ones that are specifically about working with `uN` as a "digit" (or "limb") where the output, despite being larger than can fit in a single digit, wants to be phrased in terms of those *digits*, not in terms of a wider type. (This leaves open the possibility of things like `widening_mul: u32 * u32 -> u64` for places where one wants to only think in terms of the *number*s, rather than as carries between multiple digits. Though of course discussions about how best to phrase such a thing are best for the tracking issue, not for this PR.) --- **Original PR description**: A [conversation on IRLO](https://internals.rust-lang.org/t/methods-for-splitting-integers-into-their-halves/23210/7?u=scottmcm) the other day pushed me to write this up 🙂 This PR proposes a partial stabilization of `bigint_helper_methods` (#85532), focusing on a basic set that hopefully can be non-controversial. Specifically: - [`uN::carrying_add`](https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.carrying_add): uN + uN + bool -> (uN, bool) - [`uN::widening_mul`](https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.widening_mul): uN * uN -> (uN, uN) - [`uN::carrying_mul_add`](https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.carrying_mul_add): uN * uN + uN + uN -> (uN, uN) Why these? - We should let people write Rust without needing to be backend experts to know what the magic incantation is to do this. Even `carrying_add`, which doesn't seem that complicated, actually broke in 1.82 (see #133674) so we should just offer something fit-for-purpose rather than making people keep up with whatever the secret sauce is today. We also get to do things that users cannot, like have the LLVM version emit operations on `i256` in the implementation of `u128::carrying_mul_add` (https://rust.godbolt.org/z/cjG7eKcxd). - Unsigned only because the behaviour is much clearer than when signed is involved, as everything is just unsigned (vs questions like whether `iN * iN` should give `(uN, iN)`) and carries can only happen in one direction (vs questions about whether the carry from `-128_u8 + -128_u8` should be considered `-1`). - `carrying_add` is the core [full adder](https://en.wikipedia.org/wiki/Adder_(electronics)#Full_adder) primitive for implementing addition. - `carrying_mul_add` is the core primitive for [grade school](https://en.wikipedia.org/wiki/Multiplication_algorithm#Long_multiplication) multiplication (see the example in its docs for why both carries are needed). - `widening_mul` even though it's not strictly needed (its implementation is just `carrying_mul_add(a, b, 0, 0)` right now) as the simplest way for users to get to [cranelift's `umulhi`](https://docs.rs/cranelift/latest/cranelift/prelude/trait.InstBuilder.html#method.umulhi), RISC-V's `MULHU`, Arm's `UMULL`, etc. (For example, I added an ISLE pattern bytecodealliance/wasmtime@d12e423#diff-2041f67049d5ac3d8f62ea91d3cb45cdb8608d5f5cdab988731ae2addf90ef01 so Cranelift can notice what's happening from the fallback, even if the intrinsics aren't overridden specifically. And on x86 this is one of the simplest possible non-trivial functions <https://rust.godbolt.org/z/4oadWKTc1> because `MUL` puts the results in exactly the registers that the scalar pair result happens to want.) (I did not const-stabilize them in this PR because [the fallbacks](https://github.com/rust-lang/rust/blob/master/library/core/src/intrinsics/fallback.rs) are using `#[const_trait]` plus there's two [new intrinsic](https://doc.rust-lang.org/nightly/std/intrinsics/fn.disjoint_bitor.html)s involved, so I didn't want to *also* open those cans of worms here. Given that both intrinsics *have* fallbacks, and thus don't do anything that can't already be expressed in existing Rust, const-stabilizing these should be straight-forward once the underlying machinery is allowed on stable. But that doesn't need to keep these from being usable at runtime in the mean time.)
2 parents e004014 + e49d000 commit b53c72f

File tree

1 file changed

+42
-24
lines changed

1 file changed

+42
-24
lines changed

library/core/src/num/uint_macros.rs

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,7 +2447,7 @@ macro_rules! uint_impl {
24472447
}
24482448

24492449
/// Calculates `self` + `rhs` + `carry` and returns a tuple containing
2450-
/// the sum and the output carry.
2450+
/// the sum and the output carry (in that order).
24512451
///
24522452
/// Performs "ternary addition" of two integer operands and a carry-in
24532453
/// bit, and returns an output integer and a carry-out bit. This allows
@@ -2465,8 +2465,6 @@ macro_rules! uint_impl {
24652465
/// # Examples
24662466
///
24672467
/// ```
2468-
/// #![feature(bigint_helper_methods)]
2469-
///
24702468
#[doc = concat!("// 3 MAX (a = 3 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")]
24712469
#[doc = concat!("// + 5 7 (b = 5 × 2^", stringify!($BITS), " + 7)")]
24722470
/// // ---------
@@ -2483,7 +2481,7 @@ macro_rules! uint_impl {
24832481
///
24842482
/// assert_eq!((sum1, sum0), (9, 6));
24852483
/// ```
2486-
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
2484+
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
24872485
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
24882486
#[must_use = "this returns the result of the operation, \
24892487
without modifying the original"]
@@ -2559,8 +2557,6 @@ macro_rules! uint_impl {
25592557
/// # Examples
25602558
///
25612559
/// ```
2562-
/// #![feature(bigint_helper_methods)]
2563-
///
25642560
#[doc = concat!("// 9 6 (a = 9 × 2^", stringify!($BITS), " + 6)")]
25652561
#[doc = concat!("// - 5 7 (b = 5 × 2^", stringify!($BITS), " + 7)")]
25662562
/// // ---------
@@ -2577,7 +2573,7 @@ macro_rules! uint_impl {
25772573
///
25782574
#[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")]
25792575
/// ```
2580-
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
2576+
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
25812577
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
25822578
#[must_use = "this returns the result of the operation, \
25832579
without modifying the original"]
@@ -2651,10 +2647,12 @@ macro_rules! uint_impl {
26512647
/// indicating whether an arithmetic overflow would occur. If an
26522648
/// overflow would have occurred then the wrapped value is returned.
26532649
///
2650+
/// If you want the *value* of the overflow, rather than just *whether*
2651+
/// an overflow occurred, see [`Self::carrying_mul`].
2652+
///
26542653
/// # Examples
26552654
///
2656-
/// Please note that this example is shared among integer types, which is why why `u32`
2657-
/// is used.
2655+
/// Please note that this example is shared among integer types, which is why `u32` is used.
26582656
///
26592657
/// ```
26602658
/// assert_eq!(5u32.overflowing_mul(2), (10, false));
@@ -2670,16 +2668,38 @@ macro_rules! uint_impl {
26702668
(a as Self, b)
26712669
}
26722670

2673-
/// Calculates the complete product `self * rhs` without the possibility to overflow.
2671+
/// Calculates the complete double-width product `self * rhs`.
26742672
///
26752673
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
2676-
/// of the result as two separate values, in that order.
2674+
/// of the result as two separate values, in that order. As such,
2675+
/// `a.widening_mul(b).0` produces the same result as `a.wrapping_mul(b)`.
2676+
///
2677+
/// If you also need to add a value and carry to the wide result, then you want
2678+
/// [`Self::carrying_mul_add`] instead.
26772679
///
26782680
/// If you also need to add a carry to the wide result, then you want
26792681
/// [`Self::carrying_mul`] instead.
26802682
///
2683+
/// If you just want to know *whether* the multiplication overflowed, then you
2684+
/// want [`Self::overflowing_mul`] instead.
2685+
///
26812686
/// # Examples
26822687
///
2688+
/// ```
2689+
/// #![feature(bigint_helper_methods)]
2690+
#[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".widening_mul(7), (35, 0));")]
2691+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.widening_mul(", stringify!($SelfT), "::MAX), (1, ", stringify!($SelfT), "::MAX - 1));")]
2692+
/// ```
2693+
///
2694+
/// Compared to other `*_mul` methods:
2695+
/// ```
2696+
/// #![feature(bigint_helper_methods)]
2697+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::widening_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, 3));")]
2698+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::overflowing_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, true));")]
2699+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::wrapping_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), 0);")]
2700+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::checked_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), None);")]
2701+
/// ```
2702+
///
26832703
/// Please note that this example is shared among integer types, which is why `u32` is used.
26842704
///
26852705
/// ```
@@ -2706,14 +2726,13 @@ macro_rules! uint_impl {
27062726
/// additional amount of overflow. This allows for chaining together multiple
27072727
/// multiplications to create "big integers" which represent larger values.
27082728
///
2709-
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
2729+
/// If you also need to add a value, then use [`Self::carrying_mul_add`].
27102730
///
27112731
/// # Examples
27122732
///
27132733
/// Please note that this example is shared among integer types, which is why `u32` is used.
27142734
///
27152735
/// ```
2716-
/// #![feature(bigint_helper_methods)]
27172736
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
27182737
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
27192738
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
@@ -2771,7 +2790,7 @@ macro_rules! uint_impl {
27712790
/// 789_u16.wrapping_mul(456).wrapping_add(123),
27722791
/// );
27732792
/// ```
2774-
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
2793+
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
27752794
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
27762795
#[must_use = "this returns the result of the operation, \
27772796
without modifying the original"]
@@ -2780,26 +2799,27 @@ macro_rules! uint_impl {
27802799
Self::carrying_mul_add(self, rhs, carry, 0)
27812800
}
27822801

2783-
/// Calculates the "full multiplication" `self * rhs + carry1 + carry2`
2784-
/// without the possibility to overflow.
2802+
/// Calculates the "full multiplication" `self * rhs + carry1 + carry2`.
27852803
///
27862804
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
27872805
/// of the result as two separate values, in that order.
27882806
///
2807+
/// This cannot overflow, as the double-width result has exactly enough
2808+
/// space for the largest possible result. This is equivalent to how, in
2809+
/// decimal, 9 × 9 + 9 + 9 = 81 + 18 = 99 = 9×10⁰ + 9×10¹ = 10² - 1.
2810+
///
27892811
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
27902812
/// additional amount of overflow. This allows for chaining together multiple
27912813
/// multiplications to create "big integers" which represent larger values.
27922814
///
2793-
/// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead,
2794-
/// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead.
2815+
/// If you don't need the `add` part, then you can use [`Self::carrying_mul`] instead.
27952816
///
27962817
/// # Examples
27972818
///
27982819
/// Please note that this example is shared between integer types,
27992820
/// which explains why `u32` is used here.
28002821
///
28012822
/// ```
2802-
/// #![feature(bigint_helper_methods)]
28032823
/// assert_eq!(5u32.carrying_mul_add(2, 0, 0), (10, 0));
28042824
/// assert_eq!(5u32.carrying_mul_add(2, 10, 10), (30, 0));
28052825
/// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 0, 0), (1410065408, 2));
@@ -2816,8 +2836,6 @@ macro_rules! uint_impl {
28162836
/// using `u8` for simplicity of the demonstration.
28172837
///
28182838
/// ```
2819-
/// #![feature(bigint_helper_methods)]
2820-
///
28212839
/// fn quadratic_mul<const N: usize>(a: [u8; N], b: [u8; N]) -> [u8; N] {
28222840
/// let mut out = [0; N];
28232841
/// for j in 0..N {
@@ -2832,13 +2850,13 @@ macro_rules! uint_impl {
28322850
/// // -1 * -1 == 1
28332851
/// assert_eq!(quadratic_mul([0xFF; 3], [0xFF; 3]), [1, 0, 0]);
28342852
///
2835-
/// assert_eq!(u32::wrapping_mul(0x9e3779b9, 0x7f4a7c15), 0xCFFC982D);
2853+
/// assert_eq!(u32::wrapping_mul(0x9e3779b9, 0x7f4a7c15), 0xcffc982d);
28362854
/// assert_eq!(
28372855
/// quadratic_mul(u32::to_le_bytes(0x9e3779b9), u32::to_le_bytes(0x7f4a7c15)),
2838-
/// u32::to_le_bytes(0xCFFC982D)
2856+
/// u32::to_le_bytes(0xcffc982d)
28392857
/// );
28402858
/// ```
2841-
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
2859+
#[stable(feature = "unsigned_bigint_helpers", since = "CURRENT_RUSTC_VERSION")]
28422860
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
28432861
#[must_use = "this returns the result of the operation, \
28442862
without modifying the original"]

0 commit comments

Comments
 (0)