authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-01 03:35:16+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-01 03:35:16+01:00
log9ef1050bb39cb57f4bdf4430761cfb585b4ab014
tree5f7ca15f6f19ea5db2bb3eaed1f2c35297b736b2
parent41594c19034bc609066870f2fc5011929d820ade
parentbd6f512f0c1591cce013e58ded87dcf78ad2b7a2

Merge pull request 'std.math.big.int: add log2 and log10 operations' (#31365) from hemisputnik/zig:work/13642-bigint-log2-log10 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31365 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

2 files changed, 98 insertions(+), 0 deletions(-)

lib/std/math/big/int.zig+61
...@@ -63,6 +63,11 @@ pub fn calcLimbLen(scalar: anytype) usize {...@@ -63,6 +63,11 @@ pub fn calcLimbLen(scalar: anytype) usize {
63 }63 }
64}64}
6565
66/// Same as `calcToStringLimbsBufferLen`, without the useless base check.
67pub fn calcLog10LimbsBufferLen(a_len: usize) usize {
68 return a_len + 2 + a_len + calcDivLimbsBufferLen(a_len, 1);
69}
70
66pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {71pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
67 if (math.isPowerOfTwo(base))72 if (math.isPowerOfTwo(base))
68 return 0;73 return 0;
...@@ -2670,6 +2675,62 @@ pub const Const = struct {...@@ -2670,6 +2675,62 @@ pub const Const = struct {
2670 }2675 }
2671 return @min(result, bits);2676 return @min(result, bits);
2672 }2677 }
2678
2679 /// Calculate the base 2 logarithm, rounded down.
2680 pub fn log2(a: Const) Limb {
2681 return a.limbs.len * @bitSizeOf(Limb) - 1 - @clz(a.limbs[a.limbs.len - 1]);
2682 }
2683
2684 /// Calculate the base 10 logarithm, rounded down.
2685 ///
2686 /// The allocator is used to allocate a temporary buffer.
2687 pub fn log10Alloc(a: Const, allocator: Allocator) Allocator.Error!Limb {
2688 const limbs_buffer = try allocator.alloc(Limb, calcLog10LimbsBufferLen(a.limbs.len));
2689 defer allocator.free(limbs_buffer);
2690
2691 return a.log10(limbs_buffer);
2692 }
2693
2694 /// Calculate the base 10 logarithm, rounded down.
2695 ///
2696 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcLog10LimbsBufferLen`.
2697 pub fn log10(a: Const, limbs_buffer: []Limb) Limb {
2698 const max_digits_per_limb = std.math.log10(std.math.maxInt(Limb));
2699 const limb_base = comptime calc: {
2700 var limb_base: comptime_int = 1;
2701 for (0..max_digits_per_limb) |_| limb_base *= 10;
2702 break :calc limb_base;
2703 };
2704 const limb_base_as_bigint: Const = .{ .limbs = &.{limb_base}, .positive = true };
2705
2706 var q: Mutable = .{
2707 .limbs = limbs_buffer[0 .. a.limbs.len + 2],
2708 .positive = true,
2709 .len = a.limbs.len,
2710 };
2711 @memcpy(q.limbs[0..a.limbs.len], a.limbs);
2712
2713 var remainder: Mutable = .{
2714 .limbs = limbs_buffer[q.limbs.len..][0..a.limbs.len],
2715 .positive = true,
2716 .len = 1,
2717 };
2718
2719 const division_buf = limbs_buffer[q.limbs.len + remainder.limbs.len ..];
2720
2721 var num_digits: Limb = 0;
2722 while (q.len >= 2) {
2723 q.divTrunc(&remainder, q.toConst(), limb_base_as_bigint, division_buf);
2724 num_digits += max_digits_per_limb;
2725 }
2726 var remaining_limb = q.limbs[0];
2727 while (remaining_limb != 0) {
2728 remaining_limb /= 10;
2729 num_digits += 1;
2730 }
2731
2732 return num_digits - 1;
2733 }
2673};2734};
26742735
2675/// An arbitrary-precision big integer along with an allocator which manages the memory.2736/// An arbitrary-precision big integer along with an allocator which manages the memory.
lib/std/math/big/int_test.zig+37
...@@ -4072,3 +4072,40 @@ test "ctz" {...@@ -4072,3 +4072,40 @@ test "ctz" {
4072 try testing.expectEqual(0, limb_max_squared.ctz(@bitSizeOf(Limb) * 2));4072 try testing.expectEqual(0, limb_max_squared.ctz(@bitSizeOf(Limb) * 2));
4073 try testing.expectEqual(0, limb_max_squared.ctz(@bitSizeOf(Limb) * 2 + 1));4073 try testing.expectEqual(0, limb_max_squared.ctz(@bitSizeOf(Limb) * 2 + 1));
4074}4074}
4075
4076test "log2" {
4077 var a = try Managed.init(testing.allocator);
4078 defer a.deinit();
4079
4080 try a.setString(2, "1");
4081 try testing.expectEqual(0, a.toConst().log2());
4082
4083 try a.setString(2, "1111011");
4084 try testing.expectEqual(6, a.toConst().log2());
4085
4086 try a.setString(2, "10100111011101010");
4087 try testing.expectEqual(16, a.toConst().log2());
4088
4089 try a.setString(16, "a22d71c87a9ce406da4f5895f9f3cc3d603192baf6c8a2b5c32649d0465bf188fe799b3618085e49d71bdaec01");
4090 try testing.expectEqual(359, a.toConst().log2());
4091}
4092
4093test "log10" {
4094 var a = try Managed.init(testing.allocator);
4095 defer a.deinit();
4096
4097 try a.setString(10, "1");
4098 try testing.expectEqual(0, a.toConst().log10Alloc(testing.allocator));
4099
4100 try a.setString(10, "1234");
4101 try testing.expectEqual(3, a.toConst().log10Alloc(testing.allocator));
4102
4103 try a.setString(10, "123456789");
4104 try testing.expectEqual(8, a.toConst().log10Alloc(testing.allocator));
4105
4106 try a.setString(10, "57594534510580048222343352832931567593656037535732288627581929757527496850784");
4107 try testing.expectEqual(76, a.toConst().log10Alloc(testing.allocator));
4108
4109 try a.setString(10, "504758845984192913149382719638135788792820830414213085834451043864912744833203879823150928260925344757009154551640690830148486352800955148298533547472300");
4110 try testing.expectEqual(152, a.toConst().log10Alloc(testing.allocator));
4111}