diff options
| author | tgschultz <tgschultz@gmail.com> | 2018-05-11 21:36:02 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-11 21:36:02 -0500 |
| commit | 8c1872543c8cf76215cc4bf3ced4637bb1065a4e (patch) | |
| tree | 72dfebb643ab61579e3fb8dd58cd4610ffe876fa /std/math/complex/sqrt.zig | |
| parent | 7186e92c86982950d0aa7c0c2deef9ef96bc1264 (diff) | |
| parent | 6e821078f625a03eb8b7794c983da0f7793366ab (diff) | |
| download | zig-8c1872543c8cf76215cc4bf3ced4637bb1065a4e.tar.gz zig-8c1872543c8cf76215cc4bf3ced4637bb1065a4e.zip | |
Merge pull request #1 from zig-lang/master
Sync with zig-lang/zig master
Diffstat (limited to 'std/math/complex/sqrt.zig')
| -rw-r--r-- | std/math/complex/sqrt.zig | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig new file mode 100644 index 0000000000..afda69f7c9 --- /dev/null +++ b/std/math/complex/sqrt.zig @@ -0,0 +1,133 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re)) +pub fn sqrt(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + + return switch (T) { + f32 => sqrt32(z), + f64 => sqrt64(z), + else => @compileError("sqrt not implemented for " ++ @typeName(z)), + }; +} + +fn sqrt32(z: &const Complex(f32)) Complex(f32) { + const x = z.re; + const y = z.im; + + if (x == 0 and y == 0) { + return Complex(f32).new(0, y); + } + if (math.isInf(y)) { + return Complex(f32).new(math.inf(f32), y); + } + if (math.isNan(x)) { + // raise invalid if y is not nan + const t = (y - y) / (y - y); + return Complex(f32).new(x, t); + } + if (math.isInf(x)) { + // sqrt(inf + i nan) = inf + nan i + // sqrt(inf + iy) = inf + i0 + // sqrt(-inf + i nan) = nan +- inf i + // sqrt(-inf + iy) = 0 + inf i + if (math.signbit(x)) { + return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y)); + } else { + return Complex(f32).new(x, math.copysign(f32, y - y, y)); + } + } + + // y = nan special case is handled fine below + + // double-precision avoids overflow with correct rounding. + const dx = f64(x); + const dy = f64(y); + + if (dx >= 0) { + const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); + return Complex(f32).new(f32(t), f32(dy / (2.0 * t))); + } else { + const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); + return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y))); + } +} + +fn sqrt64(z: &const Complex(f64)) Complex(f64) { + // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2)) + const threshold = 0x1.a827999fcef32p+1022; + + var x = z.re; + var y = z.im; + + if (x == 0 and y == 0) { + return Complex(f64).new(0, y); + } + if (math.isInf(y)) { + return Complex(f64).new(math.inf(f64), y); + } + if (math.isNan(x)) { + // raise invalid if y is not nan + const t = (y - y) / (y - y); + return Complex(f64).new(x, t); + } + if (math.isInf(x)) { + // sqrt(inf + i nan) = inf + nan i + // sqrt(inf + iy) = inf + i0 + // sqrt(-inf + i nan) = nan +- inf i + // sqrt(-inf + iy) = 0 + inf i + if (math.signbit(x)) { + return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y)); + } else { + return Complex(f64).new(x, math.copysign(f64, y - y, y)); + } + } + + // y = nan special case is handled fine below + + // scale to avoid overflow + var scale = false; + if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) { + x *= 0.25; + y *= 0.25; + scale = true; + } + + var result: Complex(f64) = undefined; + if (x >= 0) { + const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5); + result = Complex(f64).new(t, y / (2.0 * t)); + } else { + const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5); + result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y)); + } + + if (scale) { + result.re *= 2; + result.im *= 2; + } + + return result; +} + +const epsilon = 0.0001; + +test "complex.csqrt32" { + const a = Complex(f32).new(5, 3); + const c = sqrt(a); + + debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon)); + debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon)); +} + +test "complex.csqrt64" { + const a = Complex(f64).new(5, 3); + const c = sqrt(a); + + debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon)); + debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon)); +} |
