| 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/acoshf.c |
| 5 | // https://git.musl-libc.org/cgit/musl/tree/src/math/acosh.c |
| 6 | |
| 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; |
| 10 | |
| 11 | /// Returns the hyperbolic arc-cosine of x. |
| 12 | /// |
| 13 | /// Special cases: |
| 14 | /// - acosh(x) = nan if x < 1 |
| 15 | /// - acosh(nan) = nan |
| 16 | pub fn acosh(x: anytype) @TypeOf(x) { |
| 17 | const T = @TypeOf(x); |
| 18 | return switch (T) { |
| 19 | f32 => acosh32(x), |
| 20 | f64 => acosh64(x), |
| 21 | else => @compileError("acosh not implemented for " ++ @typeName(T)), |
| 22 | }; |
| 23 | } |
| 24 | |
| 25 | // acosh(x) = log(x + sqrt(x * x - 1)) |
| 26 | fn acosh32(x: f32) f32 { |
| 27 | const u = @as(u32, @bitCast(x)); |
| 28 | const i = u & 0x7FFFFFFF; |
| 29 | |
| 30 | // |x| < 2, invalid if x < 1 or nan |
| 31 | if (i < 0x3F800000 + (1 << 23)) { |
| 32 | return math.log1p(x - 1 + @sqrt((x - 1) * (x - 1) + 2 * (x - 1))); |
| 33 | } |
| 34 | // |x| < 0x1p12 |
| 35 | else if (i < 0x3F800000 + (12 << 23)) { |
| 36 | return @log(2 * x - 1 / (x + @sqrt(x * x - 1))); |
| 37 | } |
| 38 | // |x| >= 0x1p12 |
| 39 | else { |
| 40 | return @log(x) + 0.693147180559945309417232121458176568; |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | fn acosh64(x: f64) f64 { |
| 45 | const u = @as(u64, @bitCast(x)); |
| 46 | const e = (u >> 52) & 0x7FF; |
| 47 | |
| 48 | // |x| < 2, invalid if x < 1 or nan |
| 49 | if (e < 0x3FF + 1) { |
| 50 | return math.log1p(x - 1 + @sqrt((x - 1) * (x - 1) + 2 * (x - 1))); |
| 51 | } |
| 52 | // |x| < 0x1p26 |
| 53 | else if (e < 0x3FF + 26) { |
| 54 | return @log(2 * x - 1 / (x + @sqrt(x * x - 1))); |
| 55 | } |
| 56 | // |x| >= 0x1p26 or nan |
| 57 | else { |
| 58 | return @log(x) + 0.693147180559945309417232121458176568; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | test acosh { |
| 63 | try expect(acosh(@as(f32, 1.5)) == acosh32(1.5)); |
| 64 | try expect(acosh(@as(f64, 1.5)) == acosh64(1.5)); |
| 65 | } |
| 66 | |
| 67 | test acosh32 { |
| 68 | const epsilon = 0.000001; |
| 69 | |
| 70 | try expect(math.approxEqAbs(f32, acosh32(1.5), 0.962424, epsilon)); |
| 71 | try expect(math.approxEqAbs(f32, acosh32(37.45), 4.315976, epsilon)); |
| 72 | try expect(math.approxEqAbs(f32, acosh32(89.123), 5.183133, epsilon)); |
| 73 | try expect(math.approxEqAbs(f32, acosh32(123123.234375), 12.414088, epsilon)); |
| 74 | } |
| 75 | |
| 76 | test acosh64 { |
| 77 | const epsilon = 0.000001; |
| 78 | |
| 79 | try expect(math.approxEqAbs(f64, acosh64(1.5), 0.962424, epsilon)); |
| 80 | try expect(math.approxEqAbs(f64, acosh64(37.45), 4.315976, epsilon)); |
| 81 | try expect(math.approxEqAbs(f64, acosh64(89.123), 5.183133, epsilon)); |
| 82 | try expect(math.approxEqAbs(f64, acosh64(123123.234375), 12.414088, epsilon)); |
| 83 | } |
| 84 | |
| 85 | test "acosh32.special" { |
| 86 | try expect(math.isNan(acosh32(math.nan(f32)))); |
| 87 | try expect(math.isNan(acosh32(0.5))); |
| 88 | } |
| 89 | |
| 90 | test "acosh64.special" { |
| 91 | try expect(math.isNan(acosh64(math.nan(f64)))); |
| 92 | try expect(math.isNan(acosh64(0.5))); |
| 93 | } |