1/*
2 * Single-precision vector cosh(x) function.
3 *
4 * Copyright (c) 2022-2023, Arm Limited.
5 * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
6 */
7
8#include "v_expf_inline.h"
9#include "v_math.h"
10#include "mathlib.h"
11#include "pl_sig.h"
12#include "pl_test.h"
13
14static const struct data
15{
16  struct v_expf_data expf_consts;
17  uint32x4_t tiny_bound, special_bound;
18} data = {
19  .expf_consts = V_EXPF_DATA,
20  .tiny_bound = V4 (0x20000000), /* 0x1p-63: Round to 1 below this.  */
21  /* 0x1.5a92d8p+6: expf overflows above this, so have to use special case.  */
22  .special_bound = V4 (0x42ad496c),
23};
24
25#if !WANT_SIMD_EXCEPT
26static float32x4_t NOINLINE VPCS_ATTR
27special_case (float32x4_t x, float32x4_t y, uint32x4_t special)
28{
29  return v_call_f32 (coshf, x, y, special);
30}
31#endif
32
33/* Single-precision vector cosh, using vector expf.
34   Maximum error is 2.38 ULP:
35   _ZGVnN4v_coshf (0x1.e8001ep+1) got 0x1.6a491ep+4
36				 want 0x1.6a4922p+4.  */
37float32x4_t VPCS_ATTR V_NAME_F1 (cosh) (float32x4_t x)
38{
39  const struct data *d = ptr_barrier (&data);
40
41  float32x4_t ax = vabsq_f32 (x);
42  uint32x4_t iax = vreinterpretq_u32_f32 (ax);
43  uint32x4_t special = vcgeq_u32 (iax, d->special_bound);
44
45#if WANT_SIMD_EXCEPT
46  /* If fp exceptions are to be triggered correctly, fall back to the scalar
47     variant for all inputs if any input is a special value or above the bound
48     at which expf overflows.  */
49  if (unlikely (v_any_u32 (special)))
50    return v_call_f32 (coshf, x, x, v_u32 (-1));
51
52  uint32x4_t tiny = vcleq_u32 (iax, d->tiny_bound);
53  /* If any input is tiny, avoid underflow exception by fixing tiny lanes of
54     input to 0, which will generate no exceptions.  */
55  if (unlikely (v_any_u32 (tiny)))
56    ax = v_zerofy_f32 (ax, tiny);
57#endif
58
59  /* Calculate cosh by exp(x) / 2 + exp(-x) / 2.  */
60  float32x4_t t = v_expf_inline (ax, &d->expf_consts);
61  float32x4_t half_t = vmulq_n_f32 (t, 0.5);
62  float32x4_t half_over_t = vdivq_f32 (v_f32 (0.5), t);
63
64#if WANT_SIMD_EXCEPT
65  if (unlikely (v_any_u32 (tiny)))
66    return vbslq_f32 (tiny, v_f32 (1), vaddq_f32 (half_t, half_over_t));
67#else
68  if (unlikely (v_any_u32 (special)))
69    return special_case (x, vaddq_f32 (half_t, half_over_t), special);
70#endif
71
72  return vaddq_f32 (half_t, half_over_t);
73}
74
75PL_SIG (V, F, 1, cosh, -10.0, 10.0)
76PL_TEST_ULP (V_NAME_F1 (cosh), 1.89)
77PL_TEST_EXPECT_FENV (V_NAME_F1 (cosh), WANT_SIMD_EXCEPT)
78PL_TEST_SYM_INTERVAL (V_NAME_F1 (cosh), 0, 0x1p-63, 100)
79PL_TEST_SYM_INTERVAL (V_NAME_F1 (cosh), 0, 0x1.5a92d8p+6, 80000)
80PL_TEST_SYM_INTERVAL (V_NAME_F1 (cosh), 0x1.5a92d8p+6, inf, 2000)
81