aboutsummaryrefslogtreecommitdiff
path: root/pl/math/acoshf_2u8.c
blob: c9cded7fd2ff7f0719f9b51f561d95ab8b9b7ca6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
 * Single-precision acosh(x) function.
 *
 * Copyright (c) 2022-2023, Arm Limited.
 * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
 */

#include "math_config.h"
#include "pl_sig.h"
#include "pl_test.h"

#define Ln2 (0x1.62e4p-1f)
#define MinusZero 0x80000000
#define SquareLim 0x5f800000 /* asuint(0x1p64).  */
#define Two 0x40000000

/* Single-precision log from math/.  */
float
optr_aor_log_f32 (float);

/* Single-precision log(1+x) from pl/math.  */
float
log1pf (float);

/* acoshf approximation using a variety of approaches on different intervals:

   x >= 2^64: We cannot square x without overflow. For huge x, sqrt(x*x - 1) is
   close enough to x that we can calculate the result by ln(2x) == ln(x) +
   ln(2). The greatest error in the region is 0.94 ULP:
   acoshf(0x1.15f706p+92) got 0x1.022e14p+6 want 0x1.022e16p+6.

   x > 2: Calculate the result directly using definition of asinh(x) = ln(x +
   sqrt(x*x - 1)). Greatest error in this region is 1.30 ULP:
   acoshf(0x1.249d8p+1) got 0x1.77e1aep+0 want 0x1.77e1bp+0.

   0 <= x <= 2: Calculate the result using log1p. For x < 1, acosh(x) is
   undefined. For 1 <= x <= 2, the greatest error is 2.78 ULP:
   acoshf(0x1.07887p+0) got 0x1.ef9e9cp-3 want 0x1.ef9ea2p-3.  */
float
acoshf (float x)
{
  uint32_t ix = asuint (x);

  if (unlikely (ix >= MinusZero))
    return __math_invalidf (x);

  if (unlikely (ix >= SquareLim))
    return optr_aor_log_f32 (x) + Ln2;

  if (ix > Two)
    return optr_aor_log_f32 (x + sqrtf (x * x - 1));

  float xm1 = x - 1;
  return log1pf (xm1 + sqrtf (2 * xm1 + xm1 * xm1));
}

PL_SIG (S, F, 1, acosh, 1.0, 10.0)
PL_TEST_ULP (acoshf, 2.30)
PL_TEST_INTERVAL (acoshf, 0, 1, 100)
PL_TEST_INTERVAL (acoshf, 1, 2, 10000)
PL_TEST_INTERVAL (acoshf, 2, 0x1p64, 100000)
PL_TEST_INTERVAL (acoshf, 0x1p64, inf, 100000)
PL_TEST_INTERVAL (acoshf, -0, -inf, 10000)