zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 9feb80bd (5617B) - Raw


      1 // SPDX-License-Identifier: MIT
      2 // Copyright (c) 2015-2021 Zig Contributors
      3 // This file is part of [zig](https://ziglang.org/), which is MIT licensed.
      4 // The MIT license requires this copyright notice to be included in all copies
      5 // and substantial portions of the software.
      6 // Ported from musl, which is licensed under the MIT license:
      7 // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
      8 //
      9 // https://git.musl-libc.org/cgit/musl/tree/src/complex/csinhf.c
     10 // https://git.musl-libc.org/cgit/musl/tree/src/complex/csinh.c
     11 
     12 const std = @import("../../std.zig");
     13 const testing = std.testing;
     14 const math = std.math;
     15 const cmath = math.complex;
     16 const Complex = cmath.Complex;
     17 
     18 const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
     19 
     20 /// Returns the hyperbolic sine of z.
     21 pub fn sinh(z: anytype) @TypeOf(z) {
     22     const T = @TypeOf(z.re);
     23     return switch (T) {
     24         f32 => sinh32(z),
     25         f64 => sinh64(z),
     26         else => @compileError("tan not implemented for " ++ @typeName(z)),
     27     };
     28 }
     29 
     30 fn sinh32(z: Complex(f32)) Complex(f32) {
     31     const x = z.re;
     32     const y = z.im;
     33 
     34     const hx = @bitCast(u32, x);
     35     const ix = hx & 0x7fffffff;
     36 
     37     const hy = @bitCast(u32, y);
     38     const iy = hy & 0x7fffffff;
     39 
     40     if (ix < 0x7f800000 and iy < 0x7f800000) {
     41         if (iy == 0) {
     42             return Complex(f32).new(math.sinh(x), y);
     43         }
     44         // small x: normal case
     45         if (ix < 0x41100000) {
     46             return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
     47         }
     48 
     49         // |x|>= 9, so cosh(x) ~= exp(|x|)
     50         if (ix < 0x42b17218) {
     51             // x < 88.7: exp(|x|) won't overflow
     52             const h = math.exp(math.fabs(x)) * 0.5;
     53             return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
     54         }
     55         // x < 192.7: scale to avoid overflow
     56         else if (ix < 0x4340b1e7) {
     57             const v = Complex(f32).new(math.fabs(x), y);
     58             const r = ldexp_cexp(v, -1);
     59             return Complex(f32).new(r.re * math.copysign(f32, 1, x), r.im);
     60         }
     61         // x >= 192.7: result always overflows
     62         else {
     63             const h = 0x1p127 * x;
     64             return Complex(f32).new(h * math.cos(y), h * h * math.sin(y));
     65         }
     66     }
     67 
     68     if (ix == 0 and iy >= 0x7f800000) {
     69         return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y);
     70     }
     71 
     72     if (iy == 0 and ix >= 0x7f800000) {
     73         if (hx & 0x7fffff == 0) {
     74             return Complex(f32).new(x, y);
     75         }
     76         return Complex(f32).new(x, math.copysign(f32, 0, y));
     77     }
     78 
     79     if (ix < 0x7f800000 and iy >= 0x7f800000) {
     80         return Complex(f32).new(y - y, x * (y - y));
     81     }
     82 
     83     if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
     84         if (iy >= 0x7f800000) {
     85             return Complex(f32).new(x * x, x * (y - y));
     86         }
     87         return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y));
     88     }
     89 
     90     return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
     91 }
     92 
     93 fn sinh64(z: Complex(f64)) Complex(f64) {
     94     const x = z.re;
     95     const y = z.im;
     96 
     97     const fx = @bitCast(u64, x);
     98     const hx = @intCast(u32, fx >> 32);
     99     const lx = @truncate(u32, fx);
    100     const ix = hx & 0x7fffffff;
    101 
    102     const fy = @bitCast(u64, y);
    103     const hy = @intCast(u32, fy >> 32);
    104     const ly = @truncate(u32, fy);
    105     const iy = hy & 0x7fffffff;
    106 
    107     if (ix < 0x7ff00000 and iy < 0x7ff00000) {
    108         if (iy | ly == 0) {
    109             return Complex(f64).new(math.sinh(x), y);
    110         }
    111         // small x: normal case
    112         if (ix < 0x40360000) {
    113             return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
    114         }
    115 
    116         // |x|>= 22, so cosh(x) ~= exp(|x|)
    117         if (ix < 0x40862e42) {
    118             // x < 710: exp(|x|) won't overflow
    119             const h = math.exp(math.fabs(x)) * 0.5;
    120             return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));
    121         }
    122         // x < 1455: scale to avoid overflow
    123         else if (ix < 0x4096bbaa) {
    124             const v = Complex(f64).new(math.fabs(x), y);
    125             const r = ldexp_cexp(v, -1);
    126             return Complex(f64).new(r.re * math.copysign(f64, 1, x), r.im);
    127         }
    128         // x >= 1455: result always overflows
    129         else {
    130             const h = 0x1p1023 * x;
    131             return Complex(f64).new(h * math.cos(y), h * h * math.sin(y));
    132         }
    133     }
    134 
    135     if (ix | lx == 0 and iy >= 0x7ff00000) {
    136         return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y);
    137     }
    138 
    139     if (iy | ly == 0 and ix >= 0x7ff00000) {
    140         if ((hx & 0xfffff) | lx == 0) {
    141             return Complex(f64).new(x, y);
    142         }
    143         return Complex(f64).new(x, math.copysign(f64, 0, y));
    144     }
    145 
    146     if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
    147         return Complex(f64).new(y - y, x * (y - y));
    148     }
    149 
    150     if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
    151         if (iy >= 0x7ff00000) {
    152             return Complex(f64).new(x * x, x * (y - y));
    153         }
    154         return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y));
    155     }
    156 
    157     return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
    158 }
    159 
    160 const epsilon = 0.0001;
    161 
    162 test "complex.csinh32" {
    163     const a = Complex(f32).new(5, 3);
    164     const c = sinh(a);
    165 
    166     testing.expect(math.approxEqAbs(f32, c.re, -73.460617, epsilon));
    167     testing.expect(math.approxEqAbs(f32, c.im, 10.472508, epsilon));
    168 }
    169 
    170 test "complex.csinh64" {
    171     const a = Complex(f64).new(5, 3);
    172     const c = sinh(a);
    173 
    174     testing.expect(math.approxEqAbs(f64, c.re, -73.460617, epsilon));
    175     testing.expect(math.approxEqAbs(f64, c.im, 10.472508, epsilon));
    176 }