zig

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

blob dedd4d7e (5314B) - 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/math/asinf.c
      5 // https://git.musl-libc.org/cgit/musl/tree/src/math/asin.c
      6 
      7 const std = @import("../std.zig");
      8 const math = std.math;
      9 const 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
     16 pub 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 
     25 fn 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 
     36 fn 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 
     74 fn 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 
     91 fn 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 
    144 test "math.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 
    149 test "math.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 
    160 test "math.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 
    171 test "math.asin32.special" {
    172     try expect(asin32(0.0) == 0.0);
    173     try expect(asin32(-0.0) == -0.0);
    174     try expect(math.isNan(asin32(-2)));
    175     try expect(math.isNan(asin32(1.5)));
    176 }
    177 
    178 test "math.asin64.special" {
    179     try expect(asin64(0.0) == 0.0);
    180     try expect(asin64(-0.0) == -0.0);
    181     try expect(math.isNan(asin64(-2)));
    182     try expect(math.isNan(asin64(1.5)));
    183 }