xref: /freebsd/contrib/arm-optimized-routines/math/aarch64/advsimd/expm1.c (revision f3087bef11543b42e0d69b708f367097a4118d24)
1*f3087befSAndrew Turner /*
2*f3087befSAndrew Turner  * Double-precision vector exp(x) - 1 function.
3*f3087befSAndrew Turner  *
4*f3087befSAndrew Turner  * Copyright (c) 2022-2024, Arm Limited.
5*f3087befSAndrew Turner  * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
6*f3087befSAndrew Turner  */
7*f3087befSAndrew Turner 
8*f3087befSAndrew Turner #include "v_math.h"
9*f3087befSAndrew Turner #include "test_sig.h"
10*f3087befSAndrew Turner #include "test_defs.h"
11*f3087befSAndrew Turner #include "v_expm1_inline.h"
12*f3087befSAndrew Turner 
13*f3087befSAndrew Turner static const struct data
14*f3087befSAndrew Turner {
15*f3087befSAndrew Turner   struct v_expm1_data d;
16*f3087befSAndrew Turner #if WANT_SIMD_EXCEPT
17*f3087befSAndrew Turner   uint64x2_t thresh, tiny_bound;
18*f3087befSAndrew Turner #else
19*f3087befSAndrew Turner   float64x2_t oflow_bound;
20*f3087befSAndrew Turner #endif
21*f3087befSAndrew Turner } data = {
22*f3087befSAndrew Turner   .d = V_EXPM1_DATA,
23*f3087befSAndrew Turner #if WANT_SIMD_EXCEPT
24*f3087befSAndrew Turner   /* asuint64(oflow_bound) - asuint64(0x1p-51), shifted left by 1 for abs
25*f3087befSAndrew Turner      compare.  */
26*f3087befSAndrew Turner   .thresh = V2 (0x78c56fa6d34b552),
27*f3087befSAndrew Turner   /* asuint64(0x1p-51) << 1.  */
28*f3087befSAndrew Turner   .tiny_bound = V2 (0x3cc0000000000000 << 1),
29*f3087befSAndrew Turner #else
30*f3087befSAndrew Turner   /* Value above which expm1(x) should overflow. Absolute value of the
31*f3087befSAndrew Turner      underflow bound is greater than this, so it catches both cases - there is
32*f3087befSAndrew Turner      a small window where fallbacks are triggered unnecessarily.  */
33*f3087befSAndrew Turner   .oflow_bound = V2 (0x1.62b7d369a5aa9p+9),
34*f3087befSAndrew Turner #endif
35*f3087befSAndrew Turner };
36*f3087befSAndrew Turner 
37*f3087befSAndrew Turner static float64x2_t VPCS_ATTR NOINLINE
special_case(float64x2_t x,uint64x2_t special,const struct data * d)38*f3087befSAndrew Turner special_case (float64x2_t x, uint64x2_t special, const struct data *d)
39*f3087befSAndrew Turner {
40*f3087befSAndrew Turner   return v_call_f64 (expm1, x, expm1_inline (v_zerofy_f64 (x, special), &d->d),
41*f3087befSAndrew Turner 		     special);
42*f3087befSAndrew Turner }
43*f3087befSAndrew Turner 
44*f3087befSAndrew Turner /* Double-precision vector exp(x) - 1 function.
45*f3087befSAndrew Turner    The maximum error observed error is 2.05 ULP:
46*f3087befSAndrew Turner   _ZGVnN2v_expm1(0x1.6329669eb8c87p-2) got 0x1.a8897eef87b34p-2
47*f3087befSAndrew Turner 				      want 0x1.a8897eef87b32p-2.  */
V_NAME_D1(expm1)48*f3087befSAndrew Turner float64x2_t VPCS_ATTR V_NAME_D1 (expm1) (float64x2_t x)
49*f3087befSAndrew Turner {
50*f3087befSAndrew Turner   const struct data *d = ptr_barrier (&data);
51*f3087befSAndrew Turner 
52*f3087befSAndrew Turner #if WANT_SIMD_EXCEPT
53*f3087befSAndrew Turner   uint64x2_t ix = vreinterpretq_u64_f64 (x);
54*f3087befSAndrew Turner   /* If fp exceptions are to be triggered correctly, fall back to scalar for
55*f3087befSAndrew Turner      |x| < 2^-51, |x| > oflow_bound, Inf & NaN. Add ix to itself for
56*f3087befSAndrew Turner      shift-left by 1, and compare with thresh which was left-shifted offline -
57*f3087befSAndrew Turner      this is effectively an absolute compare.  */
58*f3087befSAndrew Turner   uint64x2_t special
59*f3087befSAndrew Turner       = vcgeq_u64 (vsubq_u64 (vaddq_u64 (ix, ix), d->tiny_bound), d->thresh);
60*f3087befSAndrew Turner #else
61*f3087befSAndrew Turner   /* Large input, NaNs and Infs.  */
62*f3087befSAndrew Turner   uint64x2_t special = vcageq_f64 (x, d->oflow_bound);
63*f3087befSAndrew Turner #endif
64*f3087befSAndrew Turner 
65*f3087befSAndrew Turner   if (unlikely (v_any_u64 (special)))
66*f3087befSAndrew Turner     return special_case (x, special, d);
67*f3087befSAndrew Turner 
68*f3087befSAndrew Turner   /* expm1(x) ~= p * t + (t - 1).  */
69*f3087befSAndrew Turner   return expm1_inline (x, &d->d);
70*f3087befSAndrew Turner }
71*f3087befSAndrew Turner 
72*f3087befSAndrew Turner TEST_SIG (V, D, 1, expm1, -9.9, 9.9)
73*f3087befSAndrew Turner TEST_ULP (V_NAME_D1 (expm1), 1.56)
74*f3087befSAndrew Turner TEST_DISABLE_FENV_IF_NOT (V_NAME_D1 (expm1), WANT_SIMD_EXCEPT)
75*f3087befSAndrew Turner TEST_SYM_INTERVAL (V_NAME_D1 (expm1), 0, 0x1p-51, 1000)
76*f3087befSAndrew Turner TEST_SYM_INTERVAL (V_NAME_D1 (expm1), 0x1p-51, 0x1.62b7d369a5aa9p+9, 100000)
77*f3087befSAndrew Turner TEST_SYM_INTERVAL (V_NAME_D1 (expm1), 0x1.62b7d369a5aa9p+9, inf, 100)
78