Skip to content

Commit d23878e

Browse files
committed
Add comptime_float support to std.math.pow
1 parent 62c6a3b commit d23878e

File tree

1 file changed

+40
-21
lines changed

1 file changed

+40
-21
lines changed

lib/std/math/pow.zig

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,20 @@ const expect = std.testing.expect;
3131
/// - pow(-inf, y) = pow(-0, -y)
3232
/// - pow(x, y) = nan for finite x < 0 and finite non-integer y
3333
pub fn pow(comptime T: type, x: T, y: T) T {
34-
if (@typeInfo(T) == .int) {
34+
const info = @typeInfo(T);
35+
if (info == .int or info == .comptime_int)
3536
return math.powi(T, x, y) catch unreachable;
36-
}
37-
38-
if (T != f32 and T != f64) {
39-
@compileError("pow not implemented for " ++ @typeName(T));
40-
}
4137

4238
// pow(x, +-0) = 1 for all x
4339
// pow(1, y) = 1 for all y
44-
if (y == 0 or x == 1) {
40+
if (y == 0 or x == 1)
4541
return 1;
46-
}
4742

4843
// pow(nan, y) = nan for all y
4944
// pow(x, nan) = nan for all x
5045
if (math.isNan(x) or math.isNan(y)) {
5146
@branchHint(.unlikely);
47+
if (info == .comptime_float) unreachable;
5248
return math.nan(T);
5349
}
5450

@@ -60,15 +56,15 @@ pub fn pow(comptime T: type, x: T, y: T) T {
6056
if (x == 0) {
6157
if (y < 0) {
6258
// pow(+-0, y) = +-inf for y an odd integer
63-
if (isOddInteger(y)) {
59+
if (isOddInteger(T, y)) {
6460
return math.copysign(math.inf(T), x);
6561
}
6662
// pow(+-0, y) = +inf for y an even integer
6763
else {
6864
return math.inf(T);
6965
}
7066
} else {
71-
if (isOddInteger(y)) {
67+
if (isOddInteger(T, y)) {
7268
return x;
7369
} else {
7470
return 0;
@@ -77,6 +73,9 @@ pub fn pow(comptime T: type, x: T, y: T) T {
7773
}
7874

7975
if (math.isInf(y)) {
76+
@branchHint(.unlikely);
77+
if (info == .comptime_float) unreachable;
78+
8079
// pow(-1, inf) = 1 for all x
8180
if (x == -1) {
8281
return 1.0;
@@ -94,6 +93,9 @@ pub fn pow(comptime T: type, x: T, y: T) T {
9493
}
9594

9695
if (math.isInf(x)) {
96+
@branchHint(.unlikely);
97+
if (info == .comptime_float) unreachable;
98+
9799
if (math.isNegativeInf(x)) {
98100
return pow(T, 1 / x, -y);
99101
}
@@ -145,7 +147,12 @@ pub fn pow(comptime T: type, x: T, y: T) T {
145147
var xe = r2.exponent;
146148
var x1 = r2.significand;
147149

148-
var i = @as(std.meta.Int(.signed, @typeInfo(T).float.bits), @intFromFloat(yi));
150+
const Int = switch (info) {
151+
.float => |float| std.meta.Int(.signed, float.bits),
152+
.comptime_float => i128,
153+
else => @compileError("pow not implemented for " ++ @typeName(T)),
154+
};
155+
var i = @as(Int, @intFromFloat(yi));
149156
while (i != 0) : (i >>= 1) {
150157
const overflow_shift = math.floatExponentBits(T) + 1;
151158
if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) {
@@ -178,25 +185,37 @@ pub fn pow(comptime T: type, x: T, y: T) T {
178185
return math.scalbn(a1, ae);
179186
}
180187

181-
fn isOddInteger(x: f64) bool {
182-
if (@abs(x) >= 1 << 53) {
188+
fn isOddInteger(comptime T: type, x: T) bool {
189+
// standard IEEE floats have an implicit 0.m or 1.m integer part
190+
// so the digits is the number of fractional bits + 1
191+
const digits = math.floatFractionalBits(T) + 1;
192+
if (@abs(x) >= digits) {
183193
// From https://golang.org/src/math/pow.go
184-
// 1 << 53 is the largest exact integer in the float64 format.
194+
// 1 << digits is the largest exact integer in the IEEE float format fN.
185195
// Any number outside this range will be truncated before the decimal point and therefore will always be
186196
// an even integer.
187-
// Without this check and if x overflows i64 the @intFromFloat(r.ipart) conversion below will panic
197+
// Without this check and if x overflows iN the @intFromFloat(r.ipart) conversion below will panic
188198
return false;
189199
}
190200
const r = math.modf(x);
191-
return r.fpart == 0.0 and @as(i64, @intFromFloat(r.ipart)) & 1 == 1;
201+
202+
const Int = switch (@typeInfo(T)) {
203+
.float => |float| std.meta.Int(.signed, float.bits),
204+
.comptime_float => i128,
205+
else => unreachable,
206+
};
207+
const ipart: Int = @intFromFloat(r.ipart);
208+
return r.fpart == 0.0 and ipart & 1 == 1;
192209
}
193210

194211
test isOddInteger {
195-
try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2)) == false);
196-
try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2 + 1)) == false);
197-
try expect(isOddInteger(1 << 53) == false);
198-
try expect(isOddInteger(12.0) == false);
199-
try expect(isOddInteger(15.0) == true);
212+
try expect(isOddInteger(f128, @floatFromInt(math.maxInt(i64) * 2)) == false);
213+
try expect(isOddInteger(comptime_float, @floatFromInt(math.maxInt(i64) * 2 + 1)) == false);
214+
try expect(isOddInteger(f64, 1 << 53) == false);
215+
try expect(isOddInteger(f80, 12.0) == false);
216+
try expect(isOddInteger(f80, 15.0) == true);
217+
try expect(isOddInteger(f32, 5.0) == true);
218+
try expect(isOddInteger(f16, -1.0) == true);
200219
}
201220

202221
test pow {

0 commit comments

Comments
 (0)