Commit c5d359e4cd

Guillaume Wenzek <gwenzek@users.noreply.github.com>
2024-01-12 17:28:56
fix #17142, wrong comptime log_int computation
1 parent d55d1e3
Changed files (3)
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) {
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");
+        }
+    }
+}
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) {