blob 5c14ec66 (3522B) - Raw
1 // Ported from musl, which is licensed under the MIT license: 2 // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 3 // 4 // https://git.musl-libc.org/cgit/musl/tree/src/complex/ctanhf.c 5 // https://git.musl-libc.org/cgit/musl/tree/src/complex/ctanh.c 6 7 const std = @import("../../std.zig"); 8 const testing = std.testing; 9 const math = std.math; 10 const cmath = math.complex; 11 const Complex = cmath.Complex; 12 13 /// Returns the hyperbolic tangent of z. 14 pub fn tanh(z: var) @typeOf(z) { 15 const T = @typeOf(z.re); 16 return switch (T) { 17 f32 => tanh32(z), 18 f64 => tanh64(z), 19 else => @compileError("tan not implemented for " ++ @typeName(z)), 20 }; 21 } 22 23 fn tanh32(z: Complex(f32)) Complex(f32) { 24 const x = z.re; 25 const y = z.im; 26 27 const hx = @bitCast(u32, x); 28 const ix = hx & 0x7fffffff; 29 30 if (ix >= 0x7f800000) { 31 if (ix & 0x7fffff != 0) { 32 const r = if (y == 0) y else x * y; 33 return Complex(f32).new(x, r); 34 } 35 const xx = @bitCast(f32, hx - 0x40000000); 36 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); 37 return Complex(f32).new(xx, math.copysign(f32, 0, r)); 38 } 39 40 if (!math.isFinite(y)) { 41 const r = if (ix != 0) y - y else x; 42 return Complex(f32).new(r, y - y); 43 } 44 45 // x >= 11 46 if (ix >= 0x41300000) { 47 const exp_mx = math.exp(-math.fabs(x)); 48 return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); 49 } 50 51 // Kahan's algorithm 52 const t = math.tan(y); 53 const beta = 1.0 + t * t; 54 const s = math.sinh(x); 55 const rho = math.sqrt(1 + s * s); 56 const den = 1 + beta * s * s; 57 58 return Complex(f32).new((beta * rho * s) / den, t / den); 59 } 60 61 fn tanh64(z: Complex(f64)) Complex(f64) { 62 const x = z.re; 63 const y = z.im; 64 65 const fx = @bitCast(u64, x); 66 // TODO: zig should allow this conversion implicitly because it can notice that the value necessarily 67 // fits in range. 68 const hx = @intCast(u32, fx >> 32); 69 const lx = @truncate(u32, fx); 70 const ix = hx & 0x7fffffff; 71 72 if (ix >= 0x7ff00000) { 73 if ((ix & 0x7fffff) | lx != 0) { 74 const r = if (y == 0) y else x * y; 75 return Complex(f64).new(x, r); 76 } 77 78 const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx); 79 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); 80 return Complex(f64).new(xx, math.copysign(f64, 0, r)); 81 } 82 83 if (!math.isFinite(y)) { 84 const r = if (ix != 0) y - y else x; 85 return Complex(f64).new(r, y - y); 86 } 87 88 // x >= 22 89 if (ix >= 0x40360000) { 90 const exp_mx = math.exp(-math.fabs(x)); 91 return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); 92 } 93 94 // Kahan's algorithm 95 const t = math.tan(y); 96 const beta = 1.0 + t * t; 97 const s = math.sinh(x); 98 const rho = math.sqrt(1 + s * s); 99 const den = 1 + beta * s * s; 100 101 return Complex(f64).new((beta * rho * s) / den, t / den); 102 } 103 104 const epsilon = 0.0001; 105 106 test "complex.ctanh32" { 107 const a = Complex(f32).new(5, 3); 108 const c = tanh(a); 109 110 testing.expect(math.approxEq(f32, c.re, 0.999913, epsilon)); 111 testing.expect(math.approxEq(f32, c.im, -0.000025, epsilon)); 112 } 113 114 test "complex.ctanh64" { 115 const a = Complex(f64).new(5, 3); 116 const c = tanh(a); 117 118 testing.expect(math.approxEq(f64, c.re, 0.999913, epsilon)); 119 testing.expect(math.approxEq(f64, c.im, -0.000025, epsilon)); 120 }