xref: /freebsd/contrib/arm-optimized-routines/pl/math/acoshf_2u8.c (revision 072a4ba82a01476eaee33781ccd241033eefcf0b)
1*072a4ba8SAndrew Turner /*
2*072a4ba8SAndrew Turner  * Single-precision acosh(x) function.
3*072a4ba8SAndrew Turner  *
4*072a4ba8SAndrew Turner  * Copyright (c) 2022-2023, Arm Limited.
5*072a4ba8SAndrew Turner  * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
6*072a4ba8SAndrew Turner  */
7*072a4ba8SAndrew Turner 
8*072a4ba8SAndrew Turner #include "math_config.h"
9*072a4ba8SAndrew Turner #include "pl_sig.h"
10*072a4ba8SAndrew Turner #include "pl_test.h"
11*072a4ba8SAndrew Turner 
12*072a4ba8SAndrew Turner #define Ln2 (0x1.62e4p-1f)
13*072a4ba8SAndrew Turner #define MinusZero 0x80000000
14*072a4ba8SAndrew Turner #define SquareLim 0x5f800000 /* asuint(0x1p64).  */
15*072a4ba8SAndrew Turner #define Two 0x40000000
16*072a4ba8SAndrew Turner 
17*072a4ba8SAndrew Turner /* Single-precision log from math/.  */
18*072a4ba8SAndrew Turner float
19*072a4ba8SAndrew Turner optr_aor_log_f32 (float);
20*072a4ba8SAndrew Turner 
21*072a4ba8SAndrew Turner /* Single-precision log(1+x) from pl/math.  */
22*072a4ba8SAndrew Turner float
23*072a4ba8SAndrew Turner log1pf (float);
24*072a4ba8SAndrew Turner 
25*072a4ba8SAndrew Turner /* acoshf approximation using a variety of approaches on different intervals:
26*072a4ba8SAndrew Turner 
27*072a4ba8SAndrew Turner    x >= 2^64: We cannot square x without overflow. For huge x, sqrt(x*x - 1) is
28*072a4ba8SAndrew Turner    close enough to x that we can calculate the result by ln(2x) == ln(x) +
29*072a4ba8SAndrew Turner    ln(2). The greatest error in the region is 0.94 ULP:
30*072a4ba8SAndrew Turner    acoshf(0x1.15f706p+92) got 0x1.022e14p+6 want 0x1.022e16p+6.
31*072a4ba8SAndrew Turner 
32*072a4ba8SAndrew Turner    x > 2: Calculate the result directly using definition of asinh(x) = ln(x +
33*072a4ba8SAndrew Turner    sqrt(x*x - 1)). Greatest error in this region is 1.30 ULP:
34*072a4ba8SAndrew Turner    acoshf(0x1.249d8p+1) got 0x1.77e1aep+0 want 0x1.77e1bp+0.
35*072a4ba8SAndrew Turner 
36*072a4ba8SAndrew Turner    0 <= x <= 2: Calculate the result using log1p. For x < 1, acosh(x) is
37*072a4ba8SAndrew Turner    undefined. For 1 <= x <= 2, the greatest error is 2.78 ULP:
38*072a4ba8SAndrew Turner    acoshf(0x1.07887p+0) got 0x1.ef9e9cp-3 want 0x1.ef9ea2p-3.  */
39*072a4ba8SAndrew Turner float
acoshf(float x)40*072a4ba8SAndrew Turner acoshf (float x)
41*072a4ba8SAndrew Turner {
42*072a4ba8SAndrew Turner   uint32_t ix = asuint (x);
43*072a4ba8SAndrew Turner 
44*072a4ba8SAndrew Turner   if (unlikely (ix >= MinusZero))
45*072a4ba8SAndrew Turner     return __math_invalidf (x);
46*072a4ba8SAndrew Turner 
47*072a4ba8SAndrew Turner   if (unlikely (ix >= SquareLim))
48*072a4ba8SAndrew Turner     return optr_aor_log_f32 (x) + Ln2;
49*072a4ba8SAndrew Turner 
50*072a4ba8SAndrew Turner   if (ix > Two)
51*072a4ba8SAndrew Turner     return optr_aor_log_f32 (x + sqrtf (x * x - 1));
52*072a4ba8SAndrew Turner 
53*072a4ba8SAndrew Turner   float xm1 = x - 1;
54*072a4ba8SAndrew Turner   return log1pf (xm1 + sqrtf (2 * xm1 + xm1 * xm1));
55*072a4ba8SAndrew Turner }
56*072a4ba8SAndrew Turner 
57*072a4ba8SAndrew Turner PL_SIG (S, F, 1, acosh, 1.0, 10.0)
58*072a4ba8SAndrew Turner PL_TEST_ULP (acoshf, 2.30)
59*072a4ba8SAndrew Turner PL_TEST_INTERVAL (acoshf, 0, 1, 100)
60*072a4ba8SAndrew Turner PL_TEST_INTERVAL (acoshf, 1, 2, 10000)
61*072a4ba8SAndrew Turner PL_TEST_INTERVAL (acoshf, 2, 0x1p64, 100000)
62*072a4ba8SAndrew Turner PL_TEST_INTERVAL (acoshf, 0x1p64, inf, 100000)
63*072a4ba8SAndrew Turner PL_TEST_INTERVAL (acoshf, -0, -inf, 10000)
64