authorgravatar for gwenzek@users.noreply.github.comGuillaume Wenzek <gwenzek@users.noreply.github.com> 2024-01-12 17:28:56+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-13 18:46:16-08:00
logc5d359e4cdcc6090f0ff1803adb929247027e7cb
tree637055ee759a05603fad17d3cced1c1fdb84e0d7
parentd55d1e32b65dd829cec17b5c5d65db10608d097c

fix #17142, wrong comptime log_int computation


3 files changed, 19 insertions(+), 6 deletions(-)

lib/std/math.zig+2
...@@ -672,6 +672,7 @@ test "rotl" {...@@ -672,6 +672,7 @@ test "rotl" {
672/// - 1. Suitable for 0-based bit indices of T.672/// - 1. Suitable for 0-based bit indices of T.
673pub fn Log2Int(comptime T: type) type {673pub fn Log2Int(comptime T: type) type {
674 // comptime ceil log2674 // comptime ceil log2
675 if (T == comptime_int) return comptime_int;
675 comptime var count = 0;676 comptime var count = 0;
676 comptime var s = @typeInfo(T).Int.bits - 1;677 comptime var s = @typeInfo(T).Int.bits - 1;
677 inline while (s != 0) : (s >>= 1) {678 inline while (s != 0) : (s >>= 1) {
...@@ -684,6 +685,7 @@ pub fn Log2Int(comptime T: type) type {...@@ -684,6 +685,7 @@ pub fn Log2Int(comptime T: type) type {
684/// Returns an unsigned int type that can hold the number of bits in T.685/// Returns an unsigned int type that can hold the number of bits in T.
685pub fn Log2IntCeil(comptime T: type) type {686pub fn Log2IntCeil(comptime T: type) type {
686 // comptime ceil log2687 // comptime ceil log2
688 if (T == comptime_int) return comptime_int;
687 comptime var count = 0;689 comptime var count = 0;
688 comptime var s = @typeInfo(T).Int.bits;690 comptime var s = @typeInfo(T).Int.bits;
689 inline while (s != 0) : (s >>= 1) {691 inline while (s != 0) : (s >>= 1) {
lib/std/math/log.zig+1-4
...@@ -24,11 +24,8 @@ pub fn log(comptime T: type, base: T, x: T) T {...@@ -24,11 +24,8 @@ pub fn log(comptime T: type, base: T, x: T) T {
24 return @as(comptime_float, @log(@as(f64, x)) / @log(float_base));24 return @as(comptime_float, @log(@as(f64, x)) / @log(float_base));
25 },25 },
2626
27 // TODO: implement integer log without using float math.
28 // The present implementation is incorrect, for example
29 // `log(comptime_int, 9, 59049)` should return `5` and not `4`.
30 .ComptimeInt => {27 .ComptimeInt => {
31 return @as(comptime_int, @floor(@log(@as(f64, x)) / @log(float_base)));28 return @as(comptime_int, math.log_int(comptime_int, base, x));
32 },29 },
3330
34 .Int => |IntType| switch (IntType.signedness) {31 .Int => |IntType| switch (IntType.signedness) {
lib/std/math/log_int.zig+16-2
...@@ -7,10 +7,15 @@ const Log2Int = math.Log2Int;...@@ -7,10 +7,15 @@ const Log2Int = math.Log2Int;
7/// Returns the logarithm of `x` for the provided `base`, rounding down to the nearest integer.7/// Returns the logarithm of `x` for the provided `base`, rounding down to the nearest integer.
8/// Asserts that `base > 1` and `x > 0`.8/// Asserts that `base > 1` and `x > 0`.
9pub fn log_int(comptime T: type, base: T, x: T) Log2Int(T) {9pub fn log_int(comptime T: type, base: T, x: T) Log2Int(T) {
10 if (@typeInfo(T) != .Int or @typeInfo(T).Int.signedness != .unsigned)10 const valid = switch (@typeInfo(T)) {
11 @compileError("log_int requires an unsigned integer, found " ++ @typeName(T));11 .ComptimeInt => true,
12 .Int => |IntType| IntType.signedness == .unsigned,
13 else => false,
14 };
15 if (!valid) @compileError("log_int requires an unsigned integer, found " ++ @typeName(T));
1216
13 assert(base > 1 and x > 0);17 assert(base > 1 and x > 0);
18 if (base == 2) return math.log2_int(T, x);
1419
15 // Let's denote by [y] the integer part of y.20 // Let's denote by [y] the integer part of y.
1621
...@@ -112,3 +117,12 @@ test "math.log_int vs math.log10" {...@@ -112,3 +117,12 @@ test "math.log_int vs math.log10" {
112 }117 }
113 }118 }
114}119}
120
121test "math.log_int at comptime" {
122 const x = 59049; // 9 ** 5;
123 comptime {
124 if (math.log_int(comptime_int, 9, x) != 5) {
125 @compileError("log(9, 59049) should be 5");
126 }
127 }
128}