authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-06-17 20:39:45-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-06-17 20:39:45-04:00
log62323eeb75b94829f0941cf97067780f0999573f
treecc167f7fa556d5b618723f8f53d046233647c471
parent4c16f9a3c35b23b9917f2a27b91ba8cd20e6fd82

std: refactor pow to be generic


4 files changed, 36 insertions(+), 190 deletions(-)

std/math/exp.zig+1-1
......@@ -11,7 +11,7 @@ pub fn exp(x: var) -> @typeOf(x) {
1111}
1212
1313fn exp32(x_: f32) -> f32 {
14 const half = []const f32 { 0.5, -0.5 };
14 const half = []f32 { 0.5, -0.5 };
1515 const ln2hi = 6.9314575195e-1;
1616 const ln2lo = 1.4286067653e-6;
1717 const invln2 = 1.4426950216e+0;
std/math/ln.zig+3-3
......@@ -120,12 +120,12 @@ fn lnd(x_: f64) -> f64 {
120120 s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi
121121}
122122
123test "log" {
123test "math.ln" {
124124 assert(ln(f32(0.2)) == lnf(0.2));
125125 assert(ln(f64(0.2)) == lnd(0.2));
126126}
127127
128test "logf" {
128test "math.ln32" {
129129 const epsilon = 0.000001;
130130
131131 assert(math.approxEq(f32, lnf(0.2), -1.609438, epsilon));
......@@ -136,7 +136,7 @@ test "logf" {
136136 assert(math.approxEq(f32, lnf(123123.234375), 11.720941, epsilon));
137137}
138138
139test "logd" {
139test "math.ln64" {
140140 const epsilon = 0.000001;
141141
142142 assert(math.approxEq(f64, lnd(0.2), -1.609438, epsilon));
std/math/pow.zig+28-177
......@@ -1,21 +1,12 @@
11const math = @import("index.zig");
22const assert = @import("../debug.zig").assert;
33
4// This implementation is taken from the go stlib, musl is a bit more complex.
45pub fn pow(comptime T: type, x: T, y: T) -> T {
5 switch (T) {
6 f32 => @inlineCall(pow32, x, y),
7 f64 => @inlineCall(pow64, x, y),
8 else => @compileError("pow not implemented for " ++ @typeName(T)),
6 if (T != f32 and T != f64) {
7 @compileError("pow not implemented for " ++ @typeName(T));
98 }
10}
119
12fn isOddInteger(x: f64) -> bool {
13 const r = math.modf(x);
14 r.fpart == 0.0 and i64(r.ipart) & 1 == 1
15}
16
17// This implementation is taken from the go stlib, musl is a bit more complex.
18fn pow32(x: f32, y: f32) -> f32 {
1910 // pow(x, +-0) = 1 for all x
2011 // pow(1, y) = 1 for all y
2112 if (y == 0 or x == 1) {
......@@ -25,7 +16,7 @@ fn pow32(x: f32, y: f32) -> f32 {
2516 // pow(nan, y) = nan for all y
2617 // pow(x, nan) = nan for all x
2718 if (math.isNan(x) or math.isNan(y)) {
28 return math.nan(f32);
19 return math.nan(T);
2920 }
3021
3122 // pow(x, 1) = x for all x
......@@ -46,11 +37,11 @@ fn pow32(x: f32, y: f32) -> f32 {
4637 if (y < 0) {
4738 // pow(+-0, y) = +- 0 for y an odd integer
4839 if (isOddInteger(y)) {
49 return math.copysign(f32, math.inf(f32), x);
40 return math.copysign(T, math.inf(T), x);
5041 }
5142 // pow(+-0, y) = +inf for y an even integer
5243 else {
53 return math.inf(f32);
44 return math.inf(T);
5445 }
5546 } else {
5647 if (isOddInteger(y)) {
......@@ -74,13 +65,13 @@ fn pow32(x: f32, y: f32) -> f32 {
7465 // pow(x, -inf) = +inf for |x| < 1
7566 // pow(x, +inf) = +inf for |x| > 1
7667 else {
77 return math.inf(f32);
68 return math.inf(T);
7869 }
7970 }
8071
8172 if (math.isInf(x)) {
8273 if (math.isNegativeInf(x)) {
83 return pow32(1 / x, -y);
74 return pow(T, 1 / x, -y);
8475 }
8576 // pow(+inf, y) = +0 for y < 0
8677 else if (y < 0) {
......@@ -88,7 +79,7 @@ fn pow32(x: f32, y: f32) -> f32 {
8879 }
8980 // pow(+inf, y) = +0 for y > 0
9081 else if (y > 0) {
91 return math.inf(f32);
82 return math.inf(T);
9283 }
9384 }
9485
......@@ -104,14 +95,14 @@ fn pow32(x: f32, y: f32) -> f32 {
10495 var yf = r1.fpart;
10596
10697 if (yf != 0 and x < 0) {
107 return math.nan(f32);
98 return math.nan(T);
10899 }
109 if (yi >= 1 << 31) {
100 if (yi >= 1 << (T.bit_count - 1)) {
110101 return math.exp(y * math.ln(x));
111102 }
112103
113104 // a = a1 * 2^ae
114 var a1: f32 = 1.0;
105 var a1: T = 1.0;
115106 var ae: i32 = 0;
116107
117108 // a *= x^yf
......@@ -151,166 +142,26 @@ fn pow32(x: f32, y: f32) -> f32 {
151142 math.scalbn(a1, ae)
152143}
153144
154// This implementation is taken from the go stlib, musl is a bit more complex.
155fn pow64(x: f64, y: f64) -> f64 {
156 // pow(x, +-0) = 1 for all x
157 // pow(1, y) = 1 for all y
158 if (y == 0 or x == 1) {
159 return 1;
160 }
161
162 // pow(nan, y) = nan for all y
163 // pow(x, nan) = nan for all x
164 if (math.isNan(x) or math.isNan(y)) {
165 return math.nan(f64);
166 }
167
168 // pow(x, 1) = x for all x
169 if (y == 1) {
170 return x;
171 }
172
173 // special case sqrt
174 if (y == 0.5) {
175 return math.sqrt(x);
176 }
177
178 if (y == -0.5) {
179 return 1 / math.sqrt(x);
180 }
181
182 if (x == 0) {
183 if (y < 0) {
184 // pow(+-0, y) = +- 0 for y an odd integer
185 if (isOddInteger(y)) {
186 return math.copysign(f64, math.inf(f64), x);
187 }
188 // pow(+-0, y) = +inf for y an even integer
189 else {
190 return math.inf(f64);
191 }
192 } else {
193 if (isOddInteger(y)) {
194 return x;
195 } else {
196 return 0;
197 }
198 }
199 }
200
201 if (math.isInf(y)) {
202 // pow(-1, inf) = -1 for all x
203 if (x == -1) {
204 return -1;
205 }
206 // pow(x, +inf) = +0 for |x| < 1
207 // pow(x, -inf) = +0 for |x| > 1
208 else if ((math.fabs(x) < 1) == math.isInf(y)) {
209 return 0;
210 }
211 // pow(x, -inf) = +inf for |x| < 1
212 // pow(x, +inf) = +inf for |x| > 1
213 else {
214 return math.inf(f64);
215 }
216 }
217
218 if (math.isInf(x)) {
219 if (math.isInf(x)) {
220 return pow64(1 / x, -y);
221 }
222 // pow(+inf, y) = +0 for y < 0
223 else if (y < 0) {
224 return 0;
225 }
226 // pow(+inf, y) = +0 for y > 0
227 else if (y > 0) {
228 return math.inf(f64);
229 }
230 }
231
232 var ay = y;
233 var flip = false;
234 if (ay < 0) {
235 ay = -ay;
236 flip = true;
237 }
238
239 const r1 = math.modf(ay);
240 var yi = r1.ipart;
241 var yf = r1.fpart;
242
243 if (yf != 0 and x < 0) {
244 return math.nan(f64);
245 }
246 if (yi >= 1 << 63) {
247 return math.exp(y * math.ln(x));
248 }
249
250 // a = a1 * 2^ae
251 var a1: f64 = 1.0;
252 var ae: i32 = 0;
253
254 // a *= x^yf
255 if (yf != 0) {
256 if (yf > 0.5) {
257 yf -= 1;
258 yi += 1;
259 }
260 a1 = math.exp(yf * math.ln(x));
261 }
262
263 // a *= x^yi
264 const r2 = math.frexp(x);
265 var xe = r2.exponent;
266 var x1 = r2.significand;
267
268 var i = i64(yi);
269 while (i != 0) : (i >>= 1) {
270 if (i & 1 == 1) {
271 a1 *= x1;
272 ae += xe;
273 }
274 x1 *= x1;
275 xe <<= 1;
276 if (x1 < 0.5) {
277 x1 += x1;
278 xe -= 1;
279 }
280 }
281
282 // a *= a1 * 2^ae
283 if (flip) {
284 a1 = 1 / a1;
285 ae = -ae;
286 }
287
288 math.scalbn(a1, ae)
289}
290
291test "pow" {
292 assert(pow(f32, 0.2, 3.3) == pow32(0.2, 3.3));
293 assert(pow(f64, 0.2, 3.3) == pow64(0.2, 3.3));
145fn isOddInteger(x: f64) -> bool {
146 const r = math.modf(x);
147 r.fpart == 0.0 and i64(r.ipart) & 1 == 1
294148}
295149
296test "pow32" {
150test "math.pow" {
297151 const epsilon = 0.000001;
298152
299 // assert(math.approxEq(f32, pow32(0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero
300 assert(math.approxEq(f32, pow32(0.8923, 3.3), 0.686572, epsilon));
301 assert(math.approxEq(f32, pow32(0.2, 3.3), 0.004936, epsilon));
302 assert(math.approxEq(f32, pow32(1.5, 3.3), 3.811546, epsilon));
303 assert(math.approxEq(f32, pow32(37.45, 3.3), 155736.703125, epsilon));
304 assert(math.approxEq(f32, pow32(89.123, 3.3), 2722489.5, epsilon));
305}
153 // assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero
154 assert(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon));
155 assert(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon));
156 assert(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon));
157 assert(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon));
158 assert(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon));
306159
307test "pow64" {
308 const epsilon = 0.000001;
309160
310 // assert(math.approxEq(f32, pow32(0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero
311 assert(math.approxEq(f64, pow64(0.8923, 3.3), 0.686572, epsilon));
312 assert(math.approxEq(f64, pow64(0.2, 3.3), 0.004936, epsilon));
313 assert(math.approxEq(f64, pow64(1.5, 3.3), 3.811546, epsilon));
314 assert(math.approxEq(f64, pow64(37.45, 3.3), 155736.7160616, epsilon));
315 assert(math.approxEq(f64, pow64(89.123, 3.3), 2722490.231436, epsilon));
161 // assert(math.approxEq(f32, pow(f64, 0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero
162 assert(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon));
163 assert(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon));
164 assert(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon));
165 assert(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon));
166 assert(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon));
316167}
std/special/builtin.zig+4-9
......@@ -60,7 +60,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
6060 if (ex == 0) {
6161 i = ux <<% exp_bits;
6262 while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<%= 1}) {}
63 ux <<%= twosComplementCast(uint, -ex + 1);
63 ux <<%= @bitCast(u32, -ex + 1);
6464 } else {
6565 ux &= @maxValue(uint) >> exp_bits;
6666 ux |= 1 <<% digits;
......@@ -68,7 +68,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
6868 if (ey == 0) {
6969 i = uy <<% exp_bits;
7070 while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<%= 1}) {}
71 uy <<= twosComplementCast(uint, -ey + 1);
71 uy <<= @bitCast(u32, -ey + 1);
7272 } else {
7373 uy &= @maxValue(uint) >> exp_bits;
7474 uy |= 1 <<% digits;
......@@ -95,9 +95,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
9595 // scale result up
9696 if (ex > 0) {
9797 ux -%= 1 <<% digits;
98 ux |= twosComplementCast(uint, ex) <<% digits;
98 ux |= @bitCast(u32, ex) <<% digits;
9999 } else {
100 ux >>= twosComplementCast(uint, -ex + 1);
100 ux >>= @bitCast(u32, -ex + 1);
101101 }
102102 if (T == f32) {
103103 ux |= sx;
......@@ -116,8 +116,3 @@ fn isNan(comptime T: type, bits: T) -> bool {
116116 unreachable;
117117 }
118118}
119
120// TODO this should be a builtin function and it shouldn't do a ptr cast
121fn twosComplementCast(comptime T: type, src: var) -> T {
122 return *@ptrCast(&const @IntType(T.is_signed, @typeOf(src).bit_count), &src);
123}