xref: /freebsd/contrib/arm-optimized-routines/math/aarch64/advsimd/sinh.c (revision dd21556857e8d40f66bf5ad54754d9d52669ebf7)
1 /*
2  * Double-precision vector sinh(x) function.
3  *
4  * Copyright (c) 2022-2024, Arm Limited.
5  * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
6  */
7 
8 #include "v_math.h"
9 #include "test_sig.h"
10 #include "test_defs.h"
11 #include "v_expm1_inline.h"
12 
13 static const struct data
14 {
15   struct v_expm1_data d;
16   uint64x2_t halff;
17 #if WANT_SIMD_EXCEPT
18   uint64x2_t tiny_bound, thresh;
19 #else
20   float64x2_t large_bound;
21 #endif
22 } data = {
23   .d = V_EXPM1_DATA,
24   .halff = V2 (0x3fe0000000000000),
25 #if WANT_SIMD_EXCEPT
26   /* 2^-26, below which sinh(x) rounds to x.  */
27   .tiny_bound = V2 (0x3e50000000000000),
28   /* asuint(large_bound) - asuint(tiny_bound).  */
29   .thresh = V2 (0x0230000000000000),
30 #else
31   /* 2^9. expm1 helper overflows for large input.  */
32   .large_bound = V2 (0x1p+9),
33 #endif
34 };
35 
36 static float64x2_t NOINLINE VPCS_ATTR
37 special_case (float64x2_t x)
38 {
39   return v_call_f64 (sinh, x, x, v_u64 (-1));
40 }
41 
42 /* Approximation for vector double-precision sinh(x) using expm1.
43    sinh(x) = (exp(x) - exp(-x)) / 2.
44    The greatest observed error is 2.52 ULP:
45    _ZGVnN2v_sinh(-0x1.a098a2177a2b9p-2) got -0x1.ac2f05bb66fccp-2
46 				       want -0x1.ac2f05bb66fc9p-2.  */
47 float64x2_t VPCS_ATTR V_NAME_D1 (sinh) (float64x2_t x)
48 {
49   const struct data *d = ptr_barrier (&data);
50 
51   float64x2_t ax = vabsq_f64 (x);
52   uint64x2_t ix = vreinterpretq_u64_f64 (x);
53   float64x2_t halfsign = vreinterpretq_f64_u64 (
54       vbslq_u64 (v_u64 (0x8000000000000000), ix, d->halff));
55 
56 #if WANT_SIMD_EXCEPT
57   uint64x2_t special = vcgeq_u64 (
58       vsubq_u64 (vreinterpretq_u64_f64 (ax), d->tiny_bound), d->thresh);
59 #else
60   uint64x2_t special = vcageq_f64 (x, d->large_bound);
61 #endif
62 
63   /* Fall back to scalar variant for all lanes if any of them are special.  */
64   if (unlikely (v_any_u64 (special)))
65     return special_case (x);
66 
67   /* Up to the point that expm1 overflows, we can use it to calculate sinh
68      using a slight rearrangement of the definition of sinh. This allows us to
69      retain acceptable accuracy for very small inputs.  */
70   float64x2_t t = expm1_inline (ax, &d->d);
71   t = vaddq_f64 (t, vdivq_f64 (t, vaddq_f64 (t, v_f64 (1.0))));
72   return vmulq_f64 (t, halfsign);
73 }
74 
75 TEST_SIG (V, D, 1, sinh, -10.0, 10.0)
76 TEST_ULP (V_NAME_D1 (sinh), 2.02)
77 TEST_DISABLE_FENV_IF_NOT (V_NAME_D1 (sinh), WANT_SIMD_EXCEPT)
78 TEST_SYM_INTERVAL (V_NAME_D1 (sinh), 0, 0x1p-26, 1000)
79 TEST_SYM_INTERVAL (V_NAME_D1 (sinh), 0x1p-26, 0x1p9, 500000)
80 TEST_SYM_INTERVAL (V_NAME_D1 (sinh), 0x1p9, inf, 1000)
81