Skip to content

Commit edba171

Browse files
committed
std.math.powi: Support comptime_int
1 parent be4eaed commit edba171

File tree

2 files changed

+49
-25
lines changed

2 files changed

+49
-25
lines changed

lib/std/fmt.zig

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,10 +540,7 @@ pub fn parseIntSizeSuffix(buf: []const u8, digit_base: u8) ParseIntError!usize {
540540
} else if (without_i.len != without_B.len) {
541541
return error.InvalidCharacter;
542542
}
543-
const multiplier = math.powi(usize, magnitude_base, orders_of_magnitude) catch |err| switch (err) {
544-
error.Underflow => unreachable,
545-
error.Overflow => return error.Overflow,
546-
};
543+
const multiplier = try math.powi(usize, magnitude_base, orders_of_magnitude);
547544
const number = try std.fmt.parseInt(usize, without_suffix, digit_base);
548545
return math.mul(usize, number, multiplier);
549546
}

lib/std/math/powi.zig

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,42 @@ const math = std.math;
88
const assert = std.debug.assert;
99
const testing = std.testing;
1010

11+
const UnsignedError = error{Overflow};
12+
const SignedError = error{
13+
Overflow,
14+
Underflow,
15+
DivisionByZero,
16+
};
17+
1118
/// Returns the power of x raised by the integer y (x^y).
1219
///
1320
/// Errors:
14-
/// - Overflow: Integer overflow or Infinity
21+
/// - Overflow: Integer overflow
1522
/// - Underflow: Absolute value of result smaller than 1
23+
/// - DivisionByZero: Undefined power.
1624
///
1725
/// Edge case rules ordered by precedence:
1826
/// - powi(T, x, 0) = 1 unless T is i1, i0, u0
1927
/// - powi(T, 0, x) = 0 when x > 0
20-
/// - powi(T, 0, x) = Overflow
28+
/// - powi(T, 0, x) = DivisionByZero
2129
/// - powi(T, 1, y) = 1
2230
/// - powi(T, -1, y) = -1 for y an odd integer
2331
/// - powi(T, -1, y) = 1 unless T is i1, i0, u0
2432
/// - powi(T, -1, y) = Overflow
2533
/// - powi(T, x, y) = Overflow when y >= @bitSizeOf(x)
2634
/// - powi(T, x, y) = Underflow when y < 0
27-
pub fn powi(comptime T: type, x: T, y: T) (error{
28-
Overflow,
29-
Underflow,
30-
}!T) {
31-
const bit_size = @typeInfo(T).int.bits;
35+
pub fn powi(comptime T: type, x: T, y: T) (if (@typeInfo(T) == .int and @typeInfo(T).int.signedness == .unsigned)
36+
UnsignedError
37+
else
38+
SignedError)!T {
39+
const info = @typeInfo(T);
40+
if (info != .int and info != .comptime_int)
41+
@compileError("powi not implemented for " ++ @typeName(T));
42+
43+
const is_unsigned = info == .int and info.int.signedness == .unsigned;
3244

3345
// `y & 1 == 0` won't compile when `does_one_overflow`.
34-
const does_one_overflow = math.maxInt(T) < 1;
46+
const does_one_overflow = info == .int and math.maxInt(T) < 1;
3547
const is_y_even = !does_one_overflow and y & 1 == 0;
3648

3749
if (x == 1 or y == 0 or (x == -1 and is_y_even)) {
@@ -50,15 +62,17 @@ pub fn powi(comptime T: type, x: T, y: T) (error{
5062
if (y > 0) {
5163
return 0;
5264
} else {
53-
// Infinity/NaN, not overflow in strict sense
54-
return error.Overflow;
65+
if (is_unsigned) unreachable;
66+
return error.DivisionByZero;
5567
}
5668
}
69+
5770
// x >= 2 or x <= -2 from this point
58-
if (y >= bit_size) {
71+
if (info == .int and y >= info.int.bits) {
5972
return error.Overflow;
6073
}
6174
if (y < 0) {
75+
if (is_unsigned) unreachable;
6276
return error.Underflow;
6377
}
6478

@@ -71,27 +85,32 @@ pub fn powi(comptime T: type, x: T, y: T) (error{
7185

7286
while (exp > 1) {
7387
if (exp & 1 == 1) {
74-
const ov = @mulWithOverflow(acc, base);
75-
if (ov[1] != 0) return error.Overflow;
76-
acc = ov[0];
88+
acc = try mul(T, acc, base);
7789
}
7890

7991
exp >>= 1;
8092

81-
const ov = @mulWithOverflow(base, base);
82-
if (ov[1] != 0) return error.Overflow;
83-
base = ov[0];
93+
base = try mul(T, base, base);
8494
}
8595

8696
if (exp == 1) {
87-
const ov = @mulWithOverflow(acc, base);
88-
if (ov[1] != 0) return error.Overflow;
89-
acc = ov[0];
97+
acc = try mul(T, acc, base);
9098
}
9199

92100
return acc;
93101
}
94102

103+
inline fn mul(comptime T: type, x: T, y: T) error{Overflow}!T {
104+
return switch (@typeInfo(T)) {
105+
.int => {
106+
const prod, const overflow = @mulWithOverflow(x, y);
107+
return if (overflow != 0) error.Overflow else prod;
108+
},
109+
.comptime_int => x * y,
110+
else => unreachable,
111+
};
112+
}
113+
95114
test powi {
96115
try testing.expectError(error.Overflow, powi(i8, -66, 6));
97116
try testing.expectError(error.Overflow, powi(i16, -13, 13));
@@ -106,13 +125,17 @@ test powi {
106125
try testing.expect((try powi(i64, -36, 6)) == 2176782336);
107126
try testing.expect((try powi(i17, -2, 15)) == -32768);
108127
try testing.expect((try powi(i42, -5, 7)) == -78125);
128+
try testing.expect((try powi(comptime_int, -12345, 11)) == -1014850422703912515858714960329315071728515625);
129+
try comptime testing.expect((try powi(comptime_int, 13, 5)) == 371293);
109130

110131
try testing.expect((try powi(u8, 6, 2)) == 36);
111132
try testing.expect((try powi(u16, 5, 4)) == 625);
112133
try testing.expect((try powi(u32, 12, 6)) == 2985984);
113134
try testing.expect((try powi(u64, 34, 2)) == 1156);
114135
try testing.expect((try powi(u17, 16, 3)) == 4096);
115136
try testing.expect((try powi(u42, 34, 6)) == 1544804416);
137+
try testing.expect((try powi(comptime_int, 54321, 9)) == 4118222497610732111054528594901610509007281);
138+
try comptime testing.expect((try powi(comptime_int, 51, 3)) == 132651);
116139

117140
try testing.expectError(error.Overflow, powi(i8, 120, 7));
118141
try testing.expectError(error.Overflow, powi(i16, 73, 15));
@@ -157,6 +180,8 @@ test "powi.special" {
157180
try testing.expect((try powi(i64, -1, 6)) == 1);
158181
try testing.expect((try powi(i17, -1, 15)) == -1);
159182
try testing.expect((try powi(i42, -1, 7)) == -1);
183+
try testing.expect((try powi(comptime_int, -1, 5)) == -1);
184+
try comptime testing.expect((try powi(comptime_int, -1, 3)) == -1);
160185

161186
try testing.expect((try powi(u8, 1, 2)) == 1);
162187
try testing.expect((try powi(u16, 1, 4)) == 1);
@@ -185,13 +210,15 @@ test "powi.special" {
185210
try testing.expect((try powi(u64, 34, 0)) == 1);
186211
try testing.expect((try powi(u17, 16, 0)) == 1);
187212
try testing.expect((try powi(u42, 34, 0)) == 1);
213+
try testing.expect((try powi(comptime_int, 41, 0)) == 1);
214+
try comptime testing.expect((try powi(comptime_int, 43, 0)) == 1);
188215
}
189216

190217
test "powi.narrow" {
191218
try testing.expectError(error.Overflow, powi(u0, 0, 0));
192219
try testing.expectError(error.Overflow, powi(i0, 0, 0));
193220
try testing.expectError(error.Overflow, powi(i1, 0, 0));
194221
try testing.expectError(error.Overflow, powi(i1, -1, 0));
195-
try testing.expectError(error.Overflow, powi(i1, 0, -1));
222+
try testing.expectError(error.DivisionByZero, powi(i1, 0, -1));
196223
try testing.expect((try powi(i1, -1, -1)) == -1);
197224
}

0 commit comments

Comments
 (0)