| ... | ... | @@ -1,21 +1,12 @@ |
| 1 | 1 | const math = @import("index.zig"); |
| 2 | 2 | const assert = @import("../debug.zig").assert; |
| 3 | 3 | |
| 4 | // This implementation is taken from the go stlib, musl is a bit more complex. |
| 4 | 5 | pub 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)); |
| 9 | 8 | } |
| 10 | | } |
| 11 | 9 | |
| 12 | | fn 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. |
| 18 | | fn pow32(x: f32, y: f32) -> f32 { |
| 19 | 10 | // pow(x, +-0) = 1 for all x |
| 20 | 11 | // pow(1, y) = 1 for all y |
| 21 | 12 | if (y == 0 or x == 1) { |
| ... | ... | @@ -25,7 +16,7 @@ fn pow32(x: f32, y: f32) -> f32 { |
| 25 | 16 | // pow(nan, y) = nan for all y |
| 26 | 17 | // pow(x, nan) = nan for all x |
| 27 | 18 | if (math.isNan(x) or math.isNan(y)) { |
| 28 | | return math.nan(f32); |
| 19 | return math.nan(T); |
| 29 | 20 | } |
| 30 | 21 | |
| 31 | 22 | // pow(x, 1) = x for all x |
| ... | ... | @@ -46,11 +37,11 @@ fn pow32(x: f32, y: f32) -> f32 { |
| 46 | 37 | if (y < 0) { |
| 47 | 38 | // pow(+-0, y) = +- 0 for y an odd integer |
| 48 | 39 | if (isOddInteger(y)) { |
| 49 | | return math.copysign(f32, math.inf(f32), x); |
| 40 | return math.copysign(T, math.inf(T), x); |
| 50 | 41 | } |
| 51 | 42 | // pow(+-0, y) = +inf for y an even integer |
| 52 | 43 | else { |
| 53 | | return math.inf(f32); |
| 44 | return math.inf(T); |
| 54 | 45 | } |
| 55 | 46 | } else { |
| 56 | 47 | if (isOddInteger(y)) { |
| ... | ... | @@ -74,13 +65,13 @@ fn pow32(x: f32, y: f32) -> f32 { |
| 74 | 65 | // pow(x, -inf) = +inf for |x| < 1 |
| 75 | 66 | // pow(x, +inf) = +inf for |x| > 1 |
| 76 | 67 | else { |
| 77 | | return math.inf(f32); |
| 68 | return math.inf(T); |
| 78 | 69 | } |
| 79 | 70 | } |
| 80 | 71 | |
| 81 | 72 | if (math.isInf(x)) { |
| 82 | 73 | if (math.isNegativeInf(x)) { |
| 83 | | return pow32(1 / x, -y); |
| 74 | return pow(T, 1 / x, -y); |
| 84 | 75 | } |
| 85 | 76 | // pow(+inf, y) = +0 for y < 0 |
| 86 | 77 | else if (y < 0) { |
| ... | ... | @@ -88,7 +79,7 @@ fn pow32(x: f32, y: f32) -> f32 { |
| 88 | 79 | } |
| 89 | 80 | // pow(+inf, y) = +0 for y > 0 |
| 90 | 81 | else if (y > 0) { |
| 91 | | return math.inf(f32); |
| 82 | return math.inf(T); |
| 92 | 83 | } |
| 93 | 84 | } |
| 94 | 85 | |
| ... | ... | @@ -104,14 +95,14 @@ fn pow32(x: f32, y: f32) -> f32 { |
| 104 | 95 | var yf = r1.fpart; |
| 105 | 96 | |
| 106 | 97 | if (yf != 0 and x < 0) { |
| 107 | | return math.nan(f32); |
| 98 | return math.nan(T); |
| 108 | 99 | } |
| 109 | | if (yi >= 1 << 31) { |
| 100 | if (yi >= 1 << (T.bit_count - 1)) { |
| 110 | 101 | return math.exp(y * math.ln(x)); |
| 111 | 102 | } |
| 112 | 103 | |
| 113 | 104 | // a = a1 * 2^ae |
| 114 | | var a1: f32 = 1.0; |
| 105 | var a1: T = 1.0; |
| 115 | 106 | var ae: i32 = 0; |
| 116 | 107 | |
| 117 | 108 | // a *= x^yf |
| ... | ... | @@ -151,166 +142,26 @@ fn pow32(x: f32, y: f32) -> f32 { |
| 151 | 142 | math.scalbn(a1, ae) |
| 152 | 143 | } |
| 153 | 144 | |
| 154 | | // This implementation is taken from the go stlib, musl is a bit more complex. |
| 155 | | fn 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 | | |
| 291 | | test "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)); |
| 145 | fn isOddInteger(x: f64) -> bool { |
| 146 | const r = math.modf(x); |
| 147 | r.fpart == 0.0 and i64(r.ipart) & 1 == 1 |
| 294 | 148 | } |
| 295 | 149 | |
| 296 | | test "pow32" { |
| 150 | test "math.pow" { |
| 297 | 151 | const epsilon = 0.000001; |
| 298 | 152 | |
| 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)); |
| 306 | 159 | |
| 307 | | test "pow64" { |
| 308 | | const epsilon = 0.000001; |
| 309 | 160 | |
| 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)); |
| 316 | 167 | } |