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