-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
What's the correct *wrapping* value for ilog{|2|10}
?
#100422
Comments
I couldn't find any previous discussion of what the exact wrapping value ought to be, just that there ought to be one, as opposed to a There was this note: #80918 (comment)
For which I think the Also, currently FWIW, the implementation in https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup gives |
In Rust we try to do the right thing. Not even Python gives -1. Let's avoid in-band error values in Rust. |
@leonardo-m What's your proposal for what the "right" thing is, then? That Arguably the most-correct value is −∞ (like So |
Python has some functions that return in-band values:
From Rust I expect something better. In Rust if a function isn't naturally total and you can't restrict its input, then I expect it to return Option (or Result), or when that's isn't ergonomic enough, a panic (in release too). Returning -1 is dangerous as that Python find function, returning 0 is not math. |
I think returning If getting closest to the "correct" math answer, how about choosing |
Perhaps in other languages. Rust is known for being better than that. I have chosen to use Rust because it's a bit better than Python (see above).
Let's return Option and and wait for const unwrap to be stabilized before making ilog const, then. |
That's what |
what about treating it like division by zero and just panicking? |
I'd be fine with -1. The point of these methods is to get fast code (otherwise many won't use them and will prefer to do If you want stricter guarantees than that, you can use |
A minor point: |
Modular arithmetic is confusing in the context of logarithm, if you want to return the discrete logarithm of 0 in the release mode, then there are a lot of candidates, because (in case of |
That's certainly a possibility. @m-ou-se suggested not doing that back in the original PR, though: #80918 (comment) |
Doesn't |
Also, since the |
I don't think it makes sense to do anything other than panic in the case of negative values, which in turn is probably an argument for panicking on zero as well. I guess one could argue that "wrapping" could be interpreted as wrapping the preimage to fit in the function's domain (rather than just wrapping the image to fit in the range) but IMO that would be entirely unintuitive and unhelpful as well. After thinking about it a bit, it seems to me that |
That's only the case on old chips. As I linked in the OP (#100422 (comment)), that happens in the instruction generation very-last-phase of LLVM on old chips, and on newer chips it knows to emit a different instruction instead of the branching code: https://rust.godbolt.org/z/1KM5zqTMG. The good news is that it looks like newer LLVM has gotten smarter here. It used to be that, because of how the intrinsic gets translated, it would emit obviously-redundant assembly on old chips in some code patterns, as you could see in Rust 1.16: https://rust.godbolt.org/z/rxPzGdxTY. |
So if we consider a wrapping I would also advocate for |
Note that's already how |
Since, from the notes, it seems like the libs-api meeting today said "how about we panic instead of wrap", I'll try to answer my own question from above:
I would propose the following rule: Conceptually, the result is first calculated in true ℤ (aka to infinite precision). Then for the non-prefixed versions
I think that's consistent with all the current precedent. (#100422 (comment) also is good reading about the distinction I'm drawing here.) |
I would redefine that to include cases where the result is defined but you can't wrap that to the result type, e.g. |
I agree, but was considering that implied by "in ℤ", as that set includes neither −∞ nor ⅈ. (Like how |
I'm not necessarily against panicking in that case. I just pointed out how it's a bit inconsistent with the other operations we have on integers. (Whether it makes sense for the other operations to have this difference in behavior between debug and release mode is an interesting discussion too, but I'm afraid that ship has sailed.) The reason we don't always panic on overflow for We applied the same reasoning to things like
That seems reasonable. It'd be nice if it'd still be easy to get the efficient |
We discussed this again in the libs-api meeting, and we concluded pretty much the same as @scottmcm's rule, meaning that these functions should panic on zero in all compilation modes, since there's not a modulo-2N-correct result. |
Panic for invalid arguments of `{integer primitive}::ilog{,2,10}` in all modes Decision made in rust-lang/rust#100422 (comment) resolves rust-lang/rust#100422 tracking issue: rust-lang/rust#70887 r? `@m-ou-se`
Filing since it's in pFCP over at #70887 (comment), and I don't think we can reasonably change it after stabilization (especially since it's documented).
Currently,
0.ilog2()
in release mode evaluates to0
. (And same for the other bases.)It's not obvious to me that that's necessarily the best option. For example, the
(BITS-1) - LeadingZeros
implementation is slightly nicer on CPUs from the past decade or so https://rust.godbolt.org/z/3q6WME84Y (and I think would be just as nice on older ones if LLVM was a bit smarter), but gives-1
as u32 in the wrapping case.It also might be nice to have the wrapping case give something clearly different from any of the successful cases, rather than having
1.ilog2()
and0.ilog2()
give the same thing. For exampleu32::MAX.next_power_of_two()
returns0
in release mode, which isn't a power of two (according tois_power_of_two
), so it's distinguishable from all the non-wrapping cases.This only affects
ilog
/ilog2
/ilog10
onuNN
. (The checked versions don't have an issue, nor do the ones onNonZeroUNN
.)The text was updated successfully, but these errors were encountered: