std.math.log_int: implement integer logarithm without using float math#17143
Conversation
98a2476 to
daafc14
Compare
| .ComptimeFloat => { | ||
| return @as(comptime_float, @log(@as(f64, x)) / @log(float_base)); | ||
| }, | ||
|
|
There was a problem hiding this comment.
Please add, that we should special case comptime-known divisions of power of two with shifts and/or make an issue.
| if (@typeInfo(T) != .Int or @typeInfo(T).Int.signedness != .unsigned) | ||
| @compileError("log_int requires an unsigned integer, found " ++ @typeName(T)); | ||
|
|
||
| assert(base > 1 and x != 0); |
There was a problem hiding this comment.
Out of curiousity: Did you test, if the assertion fires, if either base or x is known at comptime and satisfies the subexpressions?
There was a problem hiding this comment.
I'm not sure I understand the question. Do you mean whether an expression such as log_int(u8, 3, 82) panics? In this case, it does not: it returns @as(u3, 4).
Did the methods explained here https://github.com/ziglang/zig/wiki/Contributing#directly-testing-the-standard-library-with-zig-test not work for you? Please link the errors and/or explain the behavior. |
They are missing because signed integers are unsupported: pub fn log_int(comptime T: type, base: T, x: T) Log2Int(T) {
if (@typeInfo(T) != .Int or @typeInfo(T).Int.signedness != .unsigned)
@compileError("log_int requires an unsigned integer, found " ++ @typeName(T));
// ...
}This is coherent with Lines 1245 to 1247 in 4a44b79 Lines 41 to 45 in 4a44b79 |
Yep, it worked! I was missing the |
c679e40 to
c64b9bd
Compare
|
What I forgot to mention is that, apart from all the tests which are always nice to have, the algorithm is provably correct. The idea is the following.
|
|
There got to be faster algorithms than this. By repeatedly squaring the base for example, we can reduce the complexity to Quick and dirty example implementationpub fn log_int2(comptime T: type, base: T, x: T) Log2Int(T) {
if (@typeInfo(T) != .Int or @typeInfo(T).Int.signedness != .unsigned)
@compileError("log_int requires an unsigned integer, found " ++ @typeName(T));
assert(base > 1 and x != 0);
var precomputedBases: [std.math.log2_int_ceil(usize, @bitSizeOf(T))]T = undefined;
precomputedBases[0] = base;
var i: usize = 0;
while (precomputedBases[i] <= x / precomputedBases[i]) {
precomputedBases[i+1] = precomputedBases[i]*precomputedBases[i];
i += 1;
}
i += 1;
var exponent: Log2Int(T) = 0;
var power: T = 1;
while(i > 0) {
i -= 1;
if(power <= x / precomputedBases[i]) {
power *= precomputedBases[i];
exponent += @as(Log2Int(T), 1) << @intCast(i);
}
}
return exponent;
}However this will likely cause slowdowns for small |
| const Log2Int = math.Log2Int; | ||
|
|
||
| /// Returns the logarithm of `x` for the provided `base`, rounding down to the nearest integer. | ||
| /// Asserts that `base > 1` and `x != 0`. |
There was a problem hiding this comment.
Make it x > 0 then, if log_int is intended to only support unsigned integers.
I do usually provide a link to the original idea etc in a brief manner and/or very brief reasoning. See Line 18 in 8fb4a4e Not sure, if this is also needed in this case, but at least I find it usually nicer to have something on readign+reviewing the algorithms. In this case tests are mainly to uncover regressions and miscompilations (in Zig and LLVM or another backend). |
I agree, however I thought that as a starting point the priority was to replace the incorrect implementation with a correct one. |
Head branch was pushed to by a user without write access
This addresses #17142.
Important
This patch leaves the
comptime_intas is, i.e. broken. I haven't had the time to figure out where should I put the implementation for that.Given that the code for
log_intis extremely simple, it should be straightforward for someone with knowledge of the compiler to port the implementation to cover that case too.DisclaimerUnfortunately, I'm not able to run the tests locally directly from the source tree. I tested the code as a separate module, not integrated in the rest of the standard library.Edit: I managed to run the tests directly in the standard library source tree.