-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reimplement carrying_add
and borrowing_sub
for signed integers.
#93873
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -338,6 +338,32 @@ macro_rules! int_module { | |
assert_eq!(MIN.checked_next_multiple_of(-3), None); | ||
assert_eq!(MIN.checked_next_multiple_of(-1), Some(MIN)); | ||
} | ||
|
||
#[test] | ||
fn test_carrying_add() { | ||
assert_eq!($T::MAX.carrying_add(1, false), ($T::MIN, true)); | ||
assert_eq!($T::MAX.carrying_add(0, true), ($T::MIN, true)); | ||
assert_eq!($T::MAX.carrying_add(1, true), ($T::MIN + 1, true)); | ||
assert_eq!($T::MAX.carrying_add(-1, false), ($T::MAX - 1, false)); | ||
assert_eq!($T::MAX.carrying_add(-1, true), ($T::MAX, false)); // no intermediate overflow | ||
assert_eq!($T::MIN.carrying_add(-1, false), ($T::MAX, true)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I totally agree with you that these not reporting "internal" overflows is the way to go. The piece that's still unclear to me: how is it helpful for something labelled Said otherwise, I can't see a way that it's useful to pass the output There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the example I give in #85532 (comment), to implement the To summarize, when implementing bit ints, the workflow is to chain the unsigned overflow from the low order data to the higher order ones, and when performing the last operation, the result is not chained, but in fact used to indicate what happened during the operation. In my experience with the Motorola 68000, this is the intended behavior on big ints. The unsigned overflow flag is chained across I hope this explanation is clear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sorry I did not see this part of your message yesterday. I indeed forgot to update the documentation to explain why signed carrying methods should not be chained, I just updated it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, maybe it would help the conversation to have What would the signature be for that? If I'm following your comment, it sounds like it would take in an unsigned carry, because it could come from Basically, if the unsigned version is https://doc.rust-lang.org/std/primitive.u32.html#method.carrying_mul fn carrying_mul(multiplier: u32, multiplicand: u32, carry: u32) -> (u32 /* product */, u32 /*carry*/) Then it would seem weird to me if the signed version were fn carrying_mul(multiplier: i32, multiplicand: i32, carry: u32) -> (i32 /* product */, bool /*carry*/) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on an example I made, the implementations are way different between multiplication and addition/subtraction. I think the signature of fn carrying_mul(multiplier: i32, multiplicand: i32, carry: i32) -> (i32 /* product */, i32 /* carry */);
fn widening_mul(multiplier: i32, multiplicand: i32) -> (i32 /* product */, i32 /* carry */); You can see my own implementation of The idea of the implementation is that multiplying two big ints together (for example I16, which is made of an u8 and an i8):
What may look strange in signed contexts is the fact that the multiplicand is cast from unsigned to signed before being used, but it is mandatory to use signed semantics to obtain the correct result. If you want to I can provide a complete breakdown on what happens when running this code on both a signed big int and an unsigned big int. |
||
assert_eq!($T::MIN.carrying_add(-1, true), ($T::MIN, false)); // no intermediate overflow | ||
assert_eq!((0 as $T).carrying_add($T::MAX, true), ($T::MIN, true)); | ||
assert_eq!((0 as $T).carrying_add($T::MIN, true), ($T::MIN + 1, false)); | ||
} | ||
|
||
#[test] | ||
fn test_borrowing_sub() { | ||
assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true)); | ||
assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true)); | ||
assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true)); | ||
assert_eq!($T::MIN.borrowing_sub(-1, false), ($T::MIN + 1, false)); | ||
assert_eq!($T::MIN.borrowing_sub(-1, true), ($T::MIN, false)); // no intermediate overflow | ||
assert_eq!($T::MAX.borrowing_sub(-1, false), ($T::MIN, true)); | ||
assert_eq!($T::MAX.borrowing_sub(-1, true), ($T::MAX, false)); // no intermediate overflow | ||
assert_eq!((0 as $T).borrowing_sub($T::MIN, false), ($T::MIN, true)); | ||
assert_eq!((0 as $T).borrowing_sub($T::MIN, true), ($T::MAX, false)); | ||
} | ||
} | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't generate optimal code for aarch64 targets:
#85532 (comment)