-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Make Int::pow() take exp as u32 instead usize #22087
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
It would be great if the commit subject mentioned what's being done here, for example:
And then just put the "Fixes #22016" in the commit message body. |
My bad, I change that ! |
It should be possible keep the implementation as a default method if you switch from But one might argue that using |
As long as it's not merged, I can modify. However, yes, I think that the "clear" code is better in this case. It makes the reading easier (even if that's not a big problem in this case). Whatever, let's wait others' opinion. |
Consider negative exponents. With this PR, I think you can write |
@pczarn: I was actually wondering if the negative powers should be handled... |
In my opinion
Add tests to libcoretest if you choose to add these conditions. |
I'll wait other opinions before doing anything but that seems a good idea. |
It's a bit of an open question as to what the best option for an argument here is. Strictly speaking I'm not sure that we need a range outside of |
I added the changes proposed by @pczarn and tests. Now waiting for something definitive. |
Using "Self" is wrong, because exponentiation is fundamentally different from addition/multiplication (think about units, you can't have 1m ^ 1m, it just doesn't make sense). You can use any non-negative integer as the exponent without necessarily overflowing even the smallest type (1^n == 1). You might think that's not particularly useful if the only valid base is either "1" or "0", but it means you don't have to handle that case specially. Similarly the type of the exponent doesn't affect the return type (u8 ^ u64 == u8) so you don't suffer from larger integer types "leaking" into the rest of the program, as you would do if you applied this logic to the other integer operations. You may as well use the largest integer type which won't harm performance (ie. either usize or u64). Another way of looking at it, is by considering it as repeated multiplication:
There's absolutely no reason why the type of "n" should relate to the type of "base". It's limit is only related to the maximum number of times a loop can be expected to iterate (for which one would normally use the largest built-in integer type). (somewhat related: C is going to be using intmax_t for its pown function, which performs integer exponentiation on floats: http://stackoverflow.com/a/23872884) |
I think the proposition of @alexcrichton is actually the best one. Limit the pow to u8 would be more than enough. But it's nice to create a little debate sometimes ! |
@GuillaumeGomez oh to be clear I was not advocating for |
The big question which remains is: do we pass Self as exp or not ? |
On IRC I chimed in to suggest this should be u32, along with the other shifting and counting methods on Also that overflow doesn't appear to be handled #22506. |
That's what @alexcrichton came along with too. So I think I'll do it with u32. Now, it remains the following question: panic or not when overflow ? |
It makes sense to me to have whatever panic semantics integer overflow has for any other arithmetic operation. This may involve cfgs for debug/ndebug, though I honestly haven't tracked what the settled integer overflow behaviour is. |
Okay ! Then I just have to implement it ! |
cc #22240 |
acc = acc * base; | ||
if did_base_overflow { | ||
panic!("overflow detected"); | ||
} |
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.
I'm a little confused by these overflow semantics. How come the overflow is only registered on the next iteration of the loop? Additionally, how come overflow is registered at all? In theory this has the same overflow semantics as a * b
which is actually listed below (with base * base
) so isn't overflow naturally detected?
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.
When the base overflows on the last iteration, it does not corrupt the returned result and should not cause a panic. This is an edge case I discovered working on the arith-overflow branch.
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.
(Having said that, I prefer my encoding of this semantics, given in the gist linked above)
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.
That's I what I couldn't completely understand : if we panic, why should we
give attention to the returned result ?
Le 24 févr. 2015 09:54, "Felix S Klock II" notifications@github.com a
écrit :
In src/libcore/num/mod.rs
#22087 (comment):@@ -464,6 +452,30 @@ macro_rules! uint_impl {
v => Some(self / v),
}
}
+
#[inline]
fn pow(self, mut exp: u32) -> Self {
let mut base = self;
let mut acc: Self = Int::one();
let mut did_base_overflow = false;
while exp > 0 {
if (exp & 1) == 1 {
acc = acc \* base;
if did_base_overflow {
panic!("overflow detected");
}
(Having said that, I prefer my encoding of this semantics, given in the
gist linked above)—
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/pull/22087/files#r25236519.
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.
@GuillaumeGomez I fear I do not understand your question.
It seems like you are asking "why do we care about the returned result in the cases where we panic?" When we panic, of course there is no returned result. I care about enforcing the following rule: panic if and only if the accumulated acc
(the "to-be-returned result") has been corrupted by an overflow. If we panic, then I want the reason to be because the accumulated value has become useless. I do not want to panic merely because the computation base * base
overflows when its resulting value is never actually used.
From @alexcrichton 's comment, it sounds like he is assuming that your code was written in the context of a version of Rust that panics on overflow from operations like base * base
. (While on the other hand, you have written your code to still panic on overflow even in a context where base * base
itself does not.) If you look at my gist, it deliberately uses a version of multiply for the base * base
squaring computation at the end of each iteration that does not cause a panic when that squaring overflows. You would need to change your code to also use that variant if you want to ensure that the base * base
computation does not itself immediately panic.
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.
You perfectly replied to my question ! @alexcrichton gave me doubts.
Le 24 févr. 2015 11:09, "Felix S Klock II" notifications@github.com a
écrit :
In src/libcore/num/mod.rs
#22087 (comment):@@ -464,6 +452,30 @@ macro_rules! uint_impl {
v => Some(self / v),
}
}
+
#[inline]
fn pow(self, mut exp: u32) -> Self {
let mut base = self;
let mut acc: Self = Int::one();
let mut did_base_overflow = false;
while exp > 0 {
if (exp & 1) == 1 {
acc = acc \* base;
if did_base_overflow {
panic!("overflow detected");
}
@GuillaumeGomez https://github.com/GuillaumeGomez I fear I do not
understand your question.It seems like you are asking "why do we care about the returned result in
the cases where we panic?" When we panic, of course there is no returned
result. I care about enforcing the following rule: panic if and only if
the accumulated acc (the "to-be-returned result") has been corrupted by
an overflow. If we panic, then I want to to be because the accumulated
value has become useless. I do not want to panic merely because the
computation base * base overflows when its resulting value is never
actually used.From @alexcrichton https://github.com/alexcrichton 's comment, it
sounds like he is assuming that your code was written in the context of a
version of Rust that panics on overflow from operations like base * base.
(While on the other hand, you have written your code to still panic on
overflow even in a context where base * base itself does not.) If you
look at my gist, it deliberately uses a version of multiply for the base
- base squaring computation at the end of each iteration that does not
cause a panic when that squaring overflows. You would need to change your
code to also use that variant if you want to ensure that the base * base
computation does not itself immediately panic.—
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/pull/22087/files#r25241010.
@GuillaumeGomez can you change this PR to only update |
As soon as I have a computer, I do that. Oterwise he can just rebase on my
|
@GuillaumeGomez i don't know how long its going to take me to get arith-oflo landed. Please land this change on its own, in a rollup if you like, I guess. |
@GuillaumeGomez (or, if you will not have access to time or a computer in the near future, I can take over this PR for you. I largely don't want to mix this API change in with the arith-oflo work; they are distinct efforts.) |
Ok I do that (tomorrow) !
|
My actual computer is dead, i'm supposed to receive the new one by the end
|
I removed the under/overflow handling. @pnkfelix: you can take it on now ! @alexcrichton: waiting for you now. |
exp /= 2; | ||
} | ||
acc | ||
} |
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.
Would it be possible to leave the default method implementation for these methods? It looks like both impls are the same currently.
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.
It was in prevision for under/overflow handling. But if you want yes I can.
@alexcrichton: squashed |
Did some extra commits leak in? |
Finally solved... |
It looks like there are still some extra commits here (there should probably just be one) |
There isn't anymore. |
@@ -1672,6 +1673,7 @@ macro_rules! from_str_radix_int_impl { | |||
let is_signed_ty = (0 as $T) > Int::min_value(); | |||
|
|||
match src.slice_shift_char() { | |||
Some(('-', "")) => Err(PIE { kind: Empty }), |
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 seems to be leaking over from #22747
Done. |
I squashed the remaining commits and rename it. |
@bors: r+ 73ce70807e6c5f97206323429463018ab40a2eec |
⌛ Testing commit 73ce708 with merge c1e597c... |
💔 Test failed - auto-mac-32-opt |
(snippet of error, there are more) |
I updated to master and I fixed tests. Waiting for rollup. @alexcrichton @Manishearth |
`Int::pow()` takes u32 instead of usize; see rust-lang/rust#22087.
Fixes issue #22016