diff --git a/lib/std/math.zig b/lib/std/math.zig index 47ae755da0..33b4245b6b 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -672,6 +672,7 @@ test "rotl" { /// - 1. Suitable for 0-based bit indices of T. pub fn Log2Int(comptime T: type) type { // comptime ceil log2 + if (T == comptime_int) return comptime_int; comptime var count = 0; comptime var s = @typeInfo(T).Int.bits - 1; inline while (s != 0) : (s >>= 1) { @@ -684,6 +685,7 @@ pub fn Log2Int(comptime T: type) type { /// Returns an unsigned int type that can hold the number of bits in T. pub fn Log2IntCeil(comptime T: type) type { // comptime ceil log2 + if (T == comptime_int) return comptime_int; comptime var count = 0; comptime var s = @typeInfo(T).Int.bits; inline while (s != 0) : (s >>= 1) { diff --git a/lib/std/math/log.zig b/lib/std/math/log.zig index 4dcfd35d12..3063869f35 100644 --- a/lib/std/math/log.zig +++ b/lib/std/math/log.zig @@ -24,11 +24,8 @@ pub fn log(comptime T: type, base: T, x: T) T { return @as(comptime_float, @log(@as(f64, x)) / @log(float_base)); }, - // TODO: implement integer log without using float math. - // The present implementation is incorrect, for example - // `log(comptime_int, 9, 59049)` should return `5` and not `4`. .ComptimeInt => { - return @as(comptime_int, @floor(@log(@as(f64, x)) / @log(float_base))); + return @as(comptime_int, math.log_int(comptime_int, base, x)); }, .Int => |IntType| switch (IntType.signedness) { diff --git a/lib/std/math/log_int.zig b/lib/std/math/log_int.zig index d73e273d71..ea1820bd27 100644 --- a/lib/std/math/log_int.zig +++ b/lib/std/math/log_int.zig @@ -7,10 +7,15 @@ 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`. 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)); + const valid = switch (@typeInfo(T)) { + .ComptimeInt => true, + .Int => |IntType| IntType.signedness == .unsigned, + else => false, + }; + if (!valid) @compileError("log_int requires an unsigned integer, found " ++ @typeName(T)); assert(base > 1 and x > 0); + if (base == 2) return math.log2_int(T, x); // Let's denote by [y] the integer part of y. @@ -112,3 +117,12 @@ test "math.log_int vs math.log10" { } } } + +test "math.log_int at comptime" { + const x = 59049; // 9 ** 5; + comptime { + if (math.log_int(comptime_int, 9, x) != 5) { + @compileError("log(9, 59049) should be 5"); + } + } +}