master
1// Ported from go, which is licensed under a BSD-3 license.
2// https://golang.org/LICENSE
3//
4// https://golang.org/src/math/pow.go
5
6const std = @import("../std.zig");
7const math = std.math;
8const expect = std.testing.expect;
9
10/// Returns x raised to the power of y (x^y).
11///
12/// Special Cases:
13/// - pow(x, +-0) = 1 for any x
14/// - pow(1, y) = 1 for any y
15/// - pow(x, 1) = x for any x
16/// - pow(nan, y) = nan
17/// - pow(x, nan) = nan
18/// - pow(+-0, y) = +-inf for y an odd integer < 0
19/// - pow(+-0, -inf) = +inf
20/// - pow(+-0, +inf) = +0
21/// - pow(+-0, y) = +inf for finite y < 0 and not an odd integer
22/// - pow(+-0, y) = +-0 for y an odd integer > 0
23/// - pow(+-0, y) = +0 for finite y > 0 and not an odd integer
24/// - pow(-1, +-inf) = 1
25/// - pow(x, +inf) = +inf for |x| > 1
26/// - pow(x, -inf) = +0 for |x| > 1
27/// - pow(x, +inf) = +0 for |x| < 1
28/// - pow(x, -inf) = +inf for |x| < 1
29/// - pow(+inf, y) = +inf for y > 0
30/// - pow(+inf, y) = +0 for y < 0
31/// - pow(-inf, y) = pow(-0, -y)
32/// - pow(x, y) = nan for finite x < 0 and finite non-integer y
33pub fn pow(comptime T: type, x: T, y: T) T {
34 if (@typeInfo(T) == .int) {
35 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 }
41
42 // pow(x, +-0) = 1 for all x
43 // pow(1, y) = 1 for all y
44 if (y == 0 or x == 1) {
45 return 1;
46 }
47
48 // pow(nan, y) = nan for all y
49 // pow(x, nan) = nan for all x
50 if (math.isNan(x) or math.isNan(y)) {
51 @branchHint(.unlikely);
52 return math.nan(T);
53 }
54
55 // pow(x, 1) = x for all x
56 if (y == 1) {
57 return x;
58 }
59
60 if (x == 0) {
61 if (y < 0) {
62 // pow(+-0, y) = +-inf for y an odd integer
63 if (isOddInteger(y)) {
64 return math.copysign(math.inf(T), x);
65 }
66 // pow(+-0, y) = +inf for y an even integer
67 else {
68 return math.inf(T);
69 }
70 } else {
71 if (isOddInteger(y)) {
72 return x;
73 } else {
74 return 0;
75 }
76 }
77 }
78
79 if (math.isInf(y)) {
80 // pow(-1, inf) = 1 for all x
81 if (x == -1) {
82 return 1.0;
83 }
84 // pow(x, +inf) = +0 for |x| < 1
85 // pow(x, -inf) = +0 for |x| > 1
86 else if ((@abs(x) < 1) == math.isPositiveInf(y)) {
87 return 0;
88 }
89 // pow(x, -inf) = +inf for |x| < 1
90 // pow(x, +inf) = +inf for |x| > 1
91 else {
92 return math.inf(T);
93 }
94 }
95
96 if (math.isInf(x)) {
97 if (math.isNegativeInf(x)) {
98 return pow(T, 1 / x, -y);
99 }
100 // pow(+inf, y) = +0 for y < 0
101 else if (y < 0) {
102 return 0;
103 }
104 // pow(+inf, y) = +0 for y > 0
105 else if (y > 0) {
106 return math.inf(T);
107 }
108 }
109
110 // special case sqrt
111 if (y == 0.5) {
112 return @sqrt(x);
113 }
114
115 if (y == -0.5) {
116 return 1 / @sqrt(x);
117 }
118
119 const r1 = math.modf(@abs(y));
120 var yi = r1.ipart;
121 var yf = r1.fpart;
122
123 if (yf != 0 and x < 0) {
124 return math.nan(T);
125 }
126 if (yi >= 1 << (@typeInfo(T).float.bits - 1)) {
127 return @exp(y * @log(x));
128 }
129
130 // a = a1 * 2^ae
131 var a1: T = 1.0;
132 var ae: i32 = 0;
133
134 // a *= x^yf
135 if (yf != 0) {
136 if (yf > 0.5) {
137 yf -= 1;
138 yi += 1;
139 }
140 a1 = @exp(yf * @log(x));
141 }
142
143 // a *= x^yi
144 const r2 = math.frexp(x);
145 var xe = r2.exponent;
146 var x1 = r2.significand;
147
148 var i = @as(std.meta.Int(.signed, @typeInfo(T).float.bits), @intFromFloat(yi));
149 while (i != 0) : (i >>= 1) {
150 const overflow_shift = math.floatExponentBits(T) + 1;
151 if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) {
152 // catch xe before it overflows the left shift below
153 // Since i != 0 it has at least one bit still set, so ae will accumulate xe
154 // on at least one more iteration, ae += xe is a lower bound on ae
155 // the lower bound on ae exceeds the size of a float exp
156 // so the final call to Ldexp will produce under/overflow (0/Inf)
157 ae += xe;
158 break;
159 }
160 if (i & 1 == 1) {
161 a1 *= x1;
162 ae += xe;
163 }
164 x1 *= x1;
165 xe <<= 1;
166 if (x1 < 0.5) {
167 x1 += x1;
168 xe -= 1;
169 }
170 }
171
172 // a *= a1 * 2^ae
173 if (y < 0) {
174 a1 = 1 / a1;
175 ae = -ae;
176 }
177
178 return math.scalbn(a1, ae);
179}
180
181fn isOddInteger(x: f64) bool {
182 if (@abs(x) >= 1 << 53) {
183 // From https://golang.org/src/math/pow.go
184 // 1 << 53 is the largest exact integer in the float64 format.
185 // Any number outside this range will be truncated before the decimal point and therefore will always be
186 // an even integer.
187 // Without this check and if x overflows i64 the @intFromFloat(r.ipart) conversion below will panic
188 return false;
189 }
190 const r = math.modf(x);
191 return r.fpart == 0.0 and @as(i64, @intFromFloat(r.ipart)) & 1 == 1;
192}
193
194test 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);
200}
201
202test pow {
203 const epsilon = 0.000001;
204
205 try expect(math.approxEqAbs(f32, pow(f32, 0.0, 3.3), 0.0, epsilon));
206 try expect(math.approxEqAbs(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon));
207 try expect(math.approxEqAbs(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon));
208 try expect(math.approxEqAbs(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon));
209 try expect(math.approxEqAbs(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon));
210 try expect(math.approxEqAbs(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon));
211
212 try expect(math.approxEqAbs(f64, pow(f64, 0.0, 3.3), 0.0, epsilon));
213 try expect(math.approxEqAbs(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon));
214 try expect(math.approxEqAbs(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon));
215 try expect(math.approxEqAbs(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon));
216 try expect(math.approxEqAbs(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon));
217 try expect(math.approxEqAbs(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon));
218}
219
220test "special" {
221 const epsilon = 0.000001;
222
223 try expect(pow(f32, 4, 0.0) == 1.0);
224 try expect(pow(f32, 7, -0.0) == 1.0);
225 try expect(pow(f32, 45, 1.0) == 45);
226 try expect(pow(f32, -45, 1.0) == -45);
227 try expect(math.isNan(pow(f32, math.nan(f32), 5.0)));
228 try expect(math.isPositiveInf(pow(f32, -math.inf(f32), 0.5)));
229 try expect(math.isPositiveInf(pow(f32, -0.0, -0.5)));
230 try expect(pow(f32, -0.0, 0.5) == 0);
231 try expect(math.isNan(pow(f32, 5.0, math.nan(f32))));
232 try expect(math.isPositiveInf(pow(f32, 0.0, -1.0)));
233 //expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required?
234 try expect(math.isPositiveInf(pow(f32, 0.0, -math.inf(f32))));
235 try expect(math.isPositiveInf(pow(f32, -0.0, -math.inf(f32))));
236 try expect(pow(f32, 0.0, math.inf(f32)) == 0.0);
237 try expect(pow(f32, -0.0, math.inf(f32)) == 0.0);
238 try expect(math.isPositiveInf(pow(f32, 0.0, -2.0)));
239 try expect(math.isPositiveInf(pow(f32, -0.0, -2.0)));
240 try expect(pow(f32, 0.0, 1.0) == 0.0);
241 try expect(pow(f32, -0.0, 1.0) == -0.0);
242 try expect(pow(f32, 0.0, 2.0) == 0.0);
243 try expect(pow(f32, -0.0, 2.0) == 0.0);
244 try expect(math.approxEqAbs(f32, pow(f32, -1.0, math.inf(f32)), 1.0, epsilon));
245 try expect(math.approxEqAbs(f32, pow(f32, -1.0, -math.inf(f32)), 1.0, epsilon));
246 try expect(math.isPositiveInf(pow(f32, 1.2, math.inf(f32))));
247 try expect(math.isPositiveInf(pow(f32, -1.2, math.inf(f32))));
248 try expect(pow(f32, 1.2, -math.inf(f32)) == 0.0);
249 try expect(pow(f32, -1.2, -math.inf(f32)) == 0.0);
250 try expect(pow(f32, 0.2, math.inf(f32)) == 0.0);
251 try expect(pow(f32, -0.2, math.inf(f32)) == 0.0);
252 try expect(math.isPositiveInf(pow(f32, 0.2, -math.inf(f32))));
253 try expect(math.isPositiveInf(pow(f32, -0.2, -math.inf(f32))));
254 try expect(math.isPositiveInf(pow(f32, math.inf(f32), 1.0)));
255 try expect(pow(f32, math.inf(f32), -1.0) == 0.0);
256 //expect(pow(f32, -math.inf(f32), 5.0) == pow(f32, -0.0, -5.0)); TODO support negative 0?
257 try expect(pow(f32, -math.inf(f32), -5.2) == pow(f32, -0.0, 5.2));
258 try expect(math.isNan(pow(f32, -1.0, 1.2)));
259 try expect(math.isNan(pow(f32, -12.4, 78.5)));
260}
261
262test "overflow" {
263 try expect(math.isPositiveInf(pow(f64, 2, 1 << 32)));
264 try expect(pow(f64, 2, -(1 << 32)) == 0);
265 try expect(math.isNegativeInf(pow(f64, -2, (1 << 32) + 1)));
266 try expect(pow(f64, 0.5, 1 << 45) == 0);
267 try expect(math.isPositiveInf(pow(f64, 0.5, -(1 << 45))));
268}