commit bd6f512f0c1591cce013e58ded87dcf78ad2b7a2 (tree)
parent 925f82da098300ac7015d7897fc5202a7ea275a0
Author: hemisputnik <hemisputnik@proton.me>
Date: Sat, 28 Feb 2026 04:39:32 +0200
std.math.big.int: add log10
Diffstat:
2 files changed, 76 insertions(+), 0 deletions(-)
diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
@@ -63,6 +63,11 @@ pub fn calcLimbLen(scalar: anytype) usize {
}
}
+/// Same as `calcToStringLimbsBufferLen`, without the useless base check.
+pub fn calcLog10LimbsBufferLen(a_len: usize) usize {
+ return a_len + 2 + a_len + calcDivLimbsBufferLen(a_len, 1);
+}
+
pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
if (math.isPowerOfTwo(base))
return 0;
@@ -2675,6 +2680,57 @@ pub const Const = struct {
pub fn log2(a: Const) Limb {
return a.limbs.len * @bitSizeOf(Limb) - 1 - @clz(a.limbs[a.limbs.len - 1]);
}
+
+ /// Calculate the base 10 logarithm, rounded down.
+ ///
+ /// The allocator is used to allocate a temporary buffer.
+ pub fn log10Alloc(a: Const, allocator: Allocator) Allocator.Error!Limb {
+ const limbs_buffer = try allocator.alloc(Limb, calcLog10LimbsBufferLen(a.limbs.len));
+ defer allocator.free(limbs_buffer);
+
+ return a.log10(limbs_buffer);
+ }
+
+ /// Calculate the base 10 logarithm, rounded down.
+ ///
+ /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcLog10LimbsBufferLen`.
+ pub fn log10(a: Const, limbs_buffer: []Limb) Limb {
+ const max_digits_per_limb = std.math.log10(std.math.maxInt(Limb));
+ const limb_base = comptime calc: {
+ var limb_base: comptime_int = 1;
+ for (0..max_digits_per_limb) |_| limb_base *= 10;
+ break :calc limb_base;
+ };
+ const limb_base_as_bigint: Const = .{ .limbs = &.{limb_base}, .positive = true };
+
+ var q: Mutable = .{
+ .limbs = limbs_buffer[0 .. a.limbs.len + 2],
+ .positive = true,
+ .len = a.limbs.len,
+ };
+ @memcpy(q.limbs[0..a.limbs.len], a.limbs);
+
+ var remainder: Mutable = .{
+ .limbs = limbs_buffer[q.limbs.len..][0..a.limbs.len],
+ .positive = true,
+ .len = 1,
+ };
+
+ const division_buf = limbs_buffer[q.limbs.len + remainder.limbs.len ..];
+
+ var num_digits: Limb = 0;
+ while (q.len >= 2) {
+ q.divTrunc(&remainder, q.toConst(), limb_base_as_bigint, division_buf);
+ num_digits += max_digits_per_limb;
+ }
+ var remaining_limb = q.limbs[0];
+ while (remaining_limb != 0) {
+ remaining_limb /= 10;
+ num_digits += 1;
+ }
+
+ return num_digits - 1;
+ }
};
/// An arbitrary-precision big integer along with an allocator which manages the memory.
diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig
@@ -4089,3 +4089,23 @@ test "log2" {
try a.setString(16, "a22d71c87a9ce406da4f5895f9f3cc3d603192baf6c8a2b5c32649d0465bf188fe799b3618085e49d71bdaec01");
try testing.expectEqual(359, a.toConst().log2());
}
+
+test "log10" {
+ var a = try Managed.init(testing.allocator);
+ defer a.deinit();
+
+ try a.setString(10, "1");
+ try testing.expectEqual(0, a.toConst().log10Alloc(testing.allocator));
+
+ try a.setString(10, "1234");
+ try testing.expectEqual(3, a.toConst().log10Alloc(testing.allocator));
+
+ try a.setString(10, "123456789");
+ try testing.expectEqual(8, a.toConst().log10Alloc(testing.allocator));
+
+ try a.setString(10, "57594534510580048222343352832931567593656037535732288627581929757527496850784");
+ try testing.expectEqual(76, a.toConst().log10Alloc(testing.allocator));
+
+ try a.setString(10, "504758845984192913149382719638135788792820830414213085834451043864912744833203879823150928260925344757009154551640690830148486352800955148298533547472300");
+ try testing.expectEqual(152, a.toConst().log10Alloc(testing.allocator));
+}