xref: /freebsd/contrib/arm-optimized-routines/math/aarch64/sve/exp.c (revision f3087bef11543b42e0d69b708f367097a4118d24)
1*f3087befSAndrew Turner /*
2*f3087befSAndrew Turner  * Double-precision vector e^x function.
3*f3087befSAndrew Turner  *
4*f3087befSAndrew Turner  * Copyright (c) 2023-2025, 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 "sv_math.h"
9*f3087befSAndrew Turner #include "test_sig.h"
10*f3087befSAndrew Turner #include "test_defs.h"
11*f3087befSAndrew Turner 
12*f3087befSAndrew Turner static const struct data
13*f3087befSAndrew Turner {
14*f3087befSAndrew Turner   double c0, c2;
15*f3087befSAndrew Turner   double c1, c3;
16*f3087befSAndrew Turner   double ln2_hi, ln2_lo, inv_ln2, shift, thres;
17*f3087befSAndrew Turner 
18*f3087befSAndrew Turner } data = {
19*f3087befSAndrew Turner   .c0 = 0x1.fffffffffdbcdp-2,
20*f3087befSAndrew Turner   .c1 = 0x1.555555555444cp-3,
21*f3087befSAndrew Turner   .c2 = 0x1.555573c6a9f7dp-5,
22*f3087befSAndrew Turner   .c3 = 0x1.1111266d28935p-7,
23*f3087befSAndrew Turner   .ln2_hi = 0x1.62e42fefa3800p-1,
24*f3087befSAndrew Turner   .ln2_lo = 0x1.ef35793c76730p-45,
25*f3087befSAndrew Turner   /* 1/ln2.  */
26*f3087befSAndrew Turner   .inv_ln2 = 0x1.71547652b82fep+0,
27*f3087befSAndrew Turner   /* 1.5*2^46+1023. This value is further explained below.  */
28*f3087befSAndrew Turner   .shift = 0x1.800000000ffc0p+46,
29*f3087befSAndrew Turner   .thres = 704.0,
30*f3087befSAndrew Turner };
31*f3087befSAndrew Turner 
32*f3087befSAndrew Turner #define SpecialOffset 0x6000000000000000 /* 0x1p513.  */
33*f3087befSAndrew Turner /* SpecialBias1 + SpecialBias1 = asuint(1.0).  */
34*f3087befSAndrew Turner #define SpecialBias1 0x7000000000000000 /* 0x1p769.  */
35*f3087befSAndrew Turner #define SpecialBias2 0x3010000000000000 /* 0x1p-254.  */
36*f3087befSAndrew Turner 
37*f3087befSAndrew Turner /* Update of both special and non-special cases, if any special case is
38*f3087befSAndrew Turner    detected.  */
39*f3087befSAndrew Turner static inline svfloat64_t
special_case(svbool_t pg,svfloat64_t s,svfloat64_t y,svfloat64_t n)40*f3087befSAndrew Turner special_case (svbool_t pg, svfloat64_t s, svfloat64_t y, svfloat64_t n)
41*f3087befSAndrew Turner {
42*f3087befSAndrew Turner   /* s=2^n may overflow, break it up into s=s1*s2,
43*f3087befSAndrew Turner      such that exp = s + s*y can be computed as s1*(s2+s2*y)
44*f3087befSAndrew Turner      and s1*s1 overflows only if n>0.  */
45*f3087befSAndrew Turner 
46*f3087befSAndrew Turner   /* If n<=0 then set b to 0x6, 0 otherwise.  */
47*f3087befSAndrew Turner   svbool_t p_sign = svcmple (pg, n, 0.0); /* n <= 0.  */
48*f3087befSAndrew Turner   svuint64_t b
49*f3087befSAndrew Turner       = svdup_u64_z (p_sign, SpecialOffset); /* Inactive lanes set to 0.  */
50*f3087befSAndrew Turner 
51*f3087befSAndrew Turner   /* Set s1 to generate overflow depending on sign of exponent n,
52*f3087befSAndrew Turner      ie. s1 = 0x70...0 - b.  */
53*f3087befSAndrew Turner   svfloat64_t s1 = svreinterpret_f64 (svsubr_x (pg, b, SpecialBias1));
54*f3087befSAndrew Turner   /* Offset s to avoid overflow in final result if n is below threshold.
55*f3087befSAndrew Turner      ie. s2 = as_u64 (s) - 0x3010...0 + b.  */
56*f3087befSAndrew Turner   svfloat64_t s2 = svreinterpret_f64 (
57*f3087befSAndrew Turner       svadd_x (pg, svsub_x (pg, svreinterpret_u64 (s), SpecialBias2), b));
58*f3087befSAndrew Turner 
59*f3087befSAndrew Turner   /* |n| > 1280 => 2^(n) overflows.  */
60*f3087befSAndrew Turner   svbool_t p_cmp = svacgt (pg, n, 1280.0);
61*f3087befSAndrew Turner 
62*f3087befSAndrew Turner   svfloat64_t r1 = svmul_x (svptrue_b64 (), s1, s1);
63*f3087befSAndrew Turner   svfloat64_t r2 = svmla_x (pg, s2, s2, y);
64*f3087befSAndrew Turner   svfloat64_t r0 = svmul_x (svptrue_b64 (), r2, s1);
65*f3087befSAndrew Turner 
66*f3087befSAndrew Turner   return svsel (p_cmp, r1, r0);
67*f3087befSAndrew Turner }
68*f3087befSAndrew Turner 
69*f3087befSAndrew Turner /* SVE exp algorithm. Maximum measured error is 1.01ulps:
70*f3087befSAndrew Turner    SV_NAME_D1 (exp)(0x1.4619d7b04da41p+6) got 0x1.885d9acc41da7p+117
71*f3087befSAndrew Turner 					 want 0x1.885d9acc41da6p+117.  */
SV_NAME_D1(exp)72*f3087befSAndrew Turner svfloat64_t SV_NAME_D1 (exp) (svfloat64_t x, const svbool_t pg)
73*f3087befSAndrew Turner {
74*f3087befSAndrew Turner   const struct data *d = ptr_barrier (&data);
75*f3087befSAndrew Turner 
76*f3087befSAndrew Turner   svbool_t special = svacgt (pg, x, d->thres);
77*f3087befSAndrew Turner 
78*f3087befSAndrew Turner   /* Use a modifed version of the shift used for flooring, such that x/ln2 is
79*f3087befSAndrew Turner      rounded to a multiple of 2^-6=1/64, shift = 1.5 * 2^52 * 2^-6 = 1.5 *
80*f3087befSAndrew Turner      2^46.
81*f3087befSAndrew Turner 
82*f3087befSAndrew Turner      n is not an integer but can be written as n = m + i/64, with i and m
83*f3087befSAndrew Turner      integer, 0 <= i < 64 and m <= n.
84*f3087befSAndrew Turner 
85*f3087befSAndrew Turner      Bits 5:0 of z will be null every time x/ln2 reaches a new integer value
86*f3087befSAndrew Turner      (n=m, i=0), and is incremented every time z (or n) is incremented by 1/64.
87*f3087befSAndrew Turner      FEXPA expects i in bits 5:0 of the input so it can be used as index into
88*f3087befSAndrew Turner      FEXPA hardwired table T[i] = 2^(i/64) for i = 0:63, that will in turn
89*f3087befSAndrew Turner      populate the mantissa of the output. Therefore, we use u=asuint(z) as
90*f3087befSAndrew Turner      input to FEXPA.
91*f3087befSAndrew Turner 
92*f3087befSAndrew Turner      We add 1023 to the modified shift value in order to set bits 16:6 of u to
93*f3087befSAndrew Turner      1, such that once these bits are moved to the exponent of the output of
94*f3087befSAndrew Turner      FEXPA, we get the exponent of 2^n right, i.e. we get 2^m.  */
95*f3087befSAndrew Turner   svfloat64_t z = svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2);
96*f3087befSAndrew Turner   svuint64_t u = svreinterpret_u64 (z);
97*f3087befSAndrew Turner   svfloat64_t n = svsub_x (pg, z, d->shift);
98*f3087befSAndrew Turner   svfloat64_t c13 = svld1rq (svptrue_b64 (), &d->c1);
99*f3087befSAndrew Turner   /* r = x - n * ln2, r is in [-ln2/(2N), ln2/(2N)].  */
100*f3087befSAndrew Turner   svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi);
101*f3087befSAndrew Turner   svfloat64_t r = svmls_lane (x, n, ln2, 0);
102*f3087befSAndrew Turner   r = svmls_lane (r, n, ln2, 1);
103*f3087befSAndrew Turner 
104*f3087befSAndrew Turner   /* y = exp(r) - 1 ~= r + C0 r^2 + C1 r^3 + C2 r^4 + C3 r^5.  */
105*f3087befSAndrew Turner   svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r);
106*f3087befSAndrew Turner   svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), r, c13, 0);
107*f3087befSAndrew Turner   svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), r, c13, 1);
108*f3087befSAndrew Turner   svfloat64_t p04 = svmla_x (pg, p01, p23, r2);
109*f3087befSAndrew Turner   svfloat64_t y = svmla_x (pg, r, p04, r2);
110*f3087befSAndrew Turner 
111*f3087befSAndrew Turner   /* s = 2^n, computed using FEXPA. FEXPA does not propagate NaNs, so for
112*f3087befSAndrew Turner      consistent NaN handling we have to manually propagate them. This comes at
113*f3087befSAndrew Turner      significant performance cost.  */
114*f3087befSAndrew Turner   svfloat64_t s = svexpa (u);
115*f3087befSAndrew Turner 
116*f3087befSAndrew Turner   /* Assemble result as exp(x) = 2^n * exp(r).  If |x| > Thresh the
117*f3087befSAndrew Turner      multiplication may overflow, so use special case routine.  */
118*f3087befSAndrew Turner 
119*f3087befSAndrew Turner   if (unlikely (svptest_any (pg, special)))
120*f3087befSAndrew Turner     {
121*f3087befSAndrew Turner       /* FEXPA zeroes the sign bit, however the sign is meaningful to the
122*f3087befSAndrew Turner 	 special case function so needs to be copied.
123*f3087befSAndrew Turner 	 e = sign bit of u << 46.  */
124*f3087befSAndrew Turner       svuint64_t e = svand_x (pg, svlsl_x (pg, u, 46), 0x8000000000000000);
125*f3087befSAndrew Turner       /* Copy sign to s.  */
126*f3087befSAndrew Turner       s = svreinterpret_f64 (svadd_x (pg, e, svreinterpret_u64 (s)));
127*f3087befSAndrew Turner       return special_case (pg, s, y, n);
128*f3087befSAndrew Turner     }
129*f3087befSAndrew Turner 
130*f3087befSAndrew Turner   /* No special case.  */
131*f3087befSAndrew Turner   return svmla_x (pg, s, s, y);
132*f3087befSAndrew Turner }
133*f3087befSAndrew Turner 
134*f3087befSAndrew Turner TEST_SIG (SV, D, 1, exp, -9.9, 9.9)
135*f3087befSAndrew Turner TEST_ULP (SV_NAME_D1 (exp), 1.46)
136*f3087befSAndrew Turner TEST_DISABLE_FENV (SV_NAME_D1 (exp))
137*f3087befSAndrew Turner TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 0, 0x1p-23, 40000)
138*f3087befSAndrew Turner TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 0x1p-23, 1, 50000)
139*f3087befSAndrew Turner TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 1, 0x1p23, 50000)
140*f3087befSAndrew Turner TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 0x1p23, inf, 50000)
141*f3087befSAndrew Turner CLOSE_SVE_ATTR
142