master
  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/math/asinf.c
  5// https://git.musl-libc.org/cgit/musl/tree/src/math/asin.c
  6
  7const std = @import("../std.zig");
  8const math = std.math;
  9const expect = std.testing.expect;
 10
 11/// Returns the arc-sin of x.
 12///
 13/// Special Cases:
 14///  - asin(+-0) = +-0
 15///  - asin(x)   = nan if x < -1 or x > 1
 16pub fn asin(x: anytype) @TypeOf(x) {
 17    const T = @TypeOf(x);
 18    return switch (T) {
 19        f32 => asin32(x),
 20        f64 => asin64(x),
 21        else => @compileError("asin not implemented for " ++ @typeName(T)),
 22    };
 23}
 24
 25fn r32(z: f32) f32 {
 26    const pS0 = 1.6666586697e-01;
 27    const pS1 = -4.2743422091e-02;
 28    const pS2 = -8.6563630030e-03;
 29    const qS1 = -7.0662963390e-01;
 30
 31    const p = z * (pS0 + z * (pS1 + z * pS2));
 32    const q = 1.0 + z * qS1;
 33    return p / q;
 34}
 35
 36fn asin32(x: f32) f32 {
 37    const pio2 = 1.570796326794896558e+00;
 38
 39    const hx: u32 = @as(u32, @bitCast(x));
 40    const ix: u32 = hx & 0x7FFFFFFF;
 41
 42    // |x| >= 1
 43    if (ix >= 0x3F800000) {
 44        // |x| >= 1
 45        if (ix == 0x3F800000) {
 46            return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact
 47        } else {
 48            return math.nan(f32); // asin(|x| > 1) is nan
 49        }
 50    }
 51
 52    // |x| < 0.5
 53    if (ix < 0x3F000000) {
 54        // 0x1p-126 <= |x| < 0x1p-12
 55        if (ix < 0x39800000 and ix >= 0x00800000) {
 56            return x;
 57        } else {
 58            return x + x * r32(x * x);
 59        }
 60    }
 61
 62    // 1 > |x| >= 0.5
 63    const z = (1 - @abs(x)) * 0.5;
 64    const s = @sqrt(z);
 65    const fx = pio2 - 2 * (s + s * r32(z));
 66
 67    if (hx >> 31 != 0) {
 68        return -fx;
 69    } else {
 70        return fx;
 71    }
 72}
 73
 74fn r64(z: f64) f64 {
 75    const pS0: f64 = 1.66666666666666657415e-01;
 76    const pS1: f64 = -3.25565818622400915405e-01;
 77    const pS2: f64 = 2.01212532134862925881e-01;
 78    const pS3: f64 = -4.00555345006794114027e-02;
 79    const pS4: f64 = 7.91534994289814532176e-04;
 80    const pS5: f64 = 3.47933107596021167570e-05;
 81    const qS1: f64 = -2.40339491173441421878e+00;
 82    const qS2: f64 = 2.02094576023350569471e+00;
 83    const qS3: f64 = -6.88283971605453293030e-01;
 84    const qS4: f64 = 7.70381505559019352791e-02;
 85
 86    const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
 87    const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
 88    return p / q;
 89}
 90
 91fn asin64(x: f64) f64 {
 92    const pio2_hi: f64 = 1.57079632679489655800e+00;
 93    const pio2_lo: f64 = 6.12323399573676603587e-17;
 94
 95    const ux = @as(u64, @bitCast(x));
 96    const hx = @as(u32, @intCast(ux >> 32));
 97    const ix = hx & 0x7FFFFFFF;
 98
 99    // |x| >= 1 or nan
100    if (ix >= 0x3FF00000) {
101        const lx = @as(u32, @intCast(ux & 0xFFFFFFFF));
102
103        // asin(1) = +-pi/2 with inexact
104        if ((ix - 0x3FF00000) | lx == 0) {
105            return x * pio2_hi + 0x1.0p-120;
106        } else {
107            return math.nan(f64);
108        }
109    }
110
111    // |x| < 0.5
112    if (ix < 0x3FE00000) {
113        // if 0x1p-1022 <= |x| < 0x1p-26 avoid raising overflow
114        if (ix < 0x3E500000 and ix >= 0x00100000) {
115            return x;
116        } else {
117            return x + x * r64(x * x);
118        }
119    }
120
121    // 1 > |x| >= 0.5
122    const z = (1 - @abs(x)) * 0.5;
123    const s = @sqrt(z);
124    const r = r64(z);
125    var fx: f64 = undefined;
126
127    // |x| > 0.975
128    if (ix >= 0x3FEF3333) {
129        fx = pio2_hi - 2 * (s + s * r);
130    } else {
131        const jx = @as(u64, @bitCast(s));
132        const df = @as(f64, @bitCast(jx & 0xFFFFFFFF00000000));
133        const c = (z - df * df) / (s + df);
134        fx = 0.5 * pio2_hi - (2 * s * r - (pio2_lo - 2 * c) - (0.5 * pio2_hi - 2 * df));
135    }
136
137    if (hx >> 31 != 0) {
138        return -fx;
139    } else {
140        return fx;
141    }
142}
143
144test asin {
145    try expect(asin(@as(f32, 0.0)) == asin32(0.0));
146    try expect(asin(@as(f64, 0.0)) == asin64(0.0));
147}
148
149test asin32 {
150    const epsilon = 0.000001;
151
152    try expect(math.approxEqAbs(f32, asin32(0.0), 0.0, epsilon));
153    try expect(math.approxEqAbs(f32, asin32(0.2), 0.201358, epsilon));
154    try expect(math.approxEqAbs(f32, asin32(-0.2), -0.201358, epsilon));
155    try expect(math.approxEqAbs(f32, asin32(0.3434), 0.350535, epsilon));
156    try expect(math.approxEqAbs(f32, asin32(0.5), 0.523599, epsilon));
157    try expect(math.approxEqAbs(f32, asin32(0.8923), 1.102415, epsilon));
158}
159
160test asin64 {
161    const epsilon = 0.000001;
162
163    try expect(math.approxEqAbs(f64, asin64(0.0), 0.0, epsilon));
164    try expect(math.approxEqAbs(f64, asin64(0.2), 0.201358, epsilon));
165    try expect(math.approxEqAbs(f64, asin64(-0.2), -0.201358, epsilon));
166    try expect(math.approxEqAbs(f64, asin64(0.3434), 0.350535, epsilon));
167    try expect(math.approxEqAbs(f64, asin64(0.5), 0.523599, epsilon));
168    try expect(math.approxEqAbs(f64, asin64(0.8923), 1.102415, epsilon));
169}
170
171test "asin32.special" {
172    try expect(math.isPositiveZero(asin32(0.0)));
173    try expect(math.isNegativeZero(asin32(-0.0)));
174    try expect(math.isNan(asin32(-2)));
175    try expect(math.isNan(asin32(1.5)));
176}
177
178test "asin64.special" {
179    try expect(math.isPositiveZero(asin64(0.0)));
180    try expect(math.isNegativeZero(asin64(-0.0)));
181    try expect(math.isNan(asin64(-2)));
182    try expect(math.isNan(asin64(1.5)));
183}