Path: blob/main/contrib/arm-optimized-routines/math/aarch64/sve/exp.c
48378 views
/*1* Double-precision vector e^x function.2*3* Copyright (c) 2023-2025, Arm Limited.4* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception5*/67#include "sv_math.h"8#include "test_sig.h"9#include "test_defs.h"1011static const struct data12{13double c0, c2;14double c1, c3;15double ln2_hi, ln2_lo, inv_ln2, shift, thres;1617} data = {18.c0 = 0x1.fffffffffdbcdp-2,19.c1 = 0x1.555555555444cp-3,20.c2 = 0x1.555573c6a9f7dp-5,21.c3 = 0x1.1111266d28935p-7,22.ln2_hi = 0x1.62e42fefa3800p-1,23.ln2_lo = 0x1.ef35793c76730p-45,24/* 1/ln2. */25.inv_ln2 = 0x1.71547652b82fep+0,26/* 1.5*2^46+1023. This value is further explained below. */27.shift = 0x1.800000000ffc0p+46,28.thres = 704.0,29};3031#define SpecialOffset 0x6000000000000000 /* 0x1p513. */32/* SpecialBias1 + SpecialBias1 = asuint(1.0). */33#define SpecialBias1 0x7000000000000000 /* 0x1p769. */34#define SpecialBias2 0x3010000000000000 /* 0x1p-254. */3536/* Update of both special and non-special cases, if any special case is37detected. */38static inline svfloat64_t39special_case (svbool_t pg, svfloat64_t s, svfloat64_t y, svfloat64_t n)40{41/* s=2^n may overflow, break it up into s=s1*s2,42such that exp = s + s*y can be computed as s1*(s2+s2*y)43and s1*s1 overflows only if n>0. */4445/* If n<=0 then set b to 0x6, 0 otherwise. */46svbool_t p_sign = svcmple (pg, n, 0.0); /* n <= 0. */47svuint64_t b48= svdup_u64_z (p_sign, SpecialOffset); /* Inactive lanes set to 0. */4950/* Set s1 to generate overflow depending on sign of exponent n,51ie. s1 = 0x70...0 - b. */52svfloat64_t s1 = svreinterpret_f64 (svsubr_x (pg, b, SpecialBias1));53/* Offset s to avoid overflow in final result if n is below threshold.54ie. s2 = as_u64 (s) - 0x3010...0 + b. */55svfloat64_t s2 = svreinterpret_f64 (56svadd_x (pg, svsub_x (pg, svreinterpret_u64 (s), SpecialBias2), b));5758/* |n| > 1280 => 2^(n) overflows. */59svbool_t p_cmp = svacgt (pg, n, 1280.0);6061svfloat64_t r1 = svmul_x (svptrue_b64 (), s1, s1);62svfloat64_t r2 = svmla_x (pg, s2, s2, y);63svfloat64_t r0 = svmul_x (svptrue_b64 (), r2, s1);6465return svsel (p_cmp, r1, r0);66}6768/* SVE exp algorithm. Maximum measured error is 1.01ulps:69SV_NAME_D1 (exp)(0x1.4619d7b04da41p+6) got 0x1.885d9acc41da7p+11770want 0x1.885d9acc41da6p+117. */71svfloat64_t SV_NAME_D1 (exp) (svfloat64_t x, const svbool_t pg)72{73const struct data *d = ptr_barrier (&data);7475svbool_t special = svacgt (pg, x, d->thres);7677/* Use a modifed version of the shift used for flooring, such that x/ln2 is78rounded to a multiple of 2^-6=1/64, shift = 1.5 * 2^52 * 2^-6 = 1.5 *792^46.8081n is not an integer but can be written as n = m + i/64, with i and m82integer, 0 <= i < 64 and m <= n.8384Bits 5:0 of z will be null every time x/ln2 reaches a new integer value85(n=m, i=0), and is incremented every time z (or n) is incremented by 1/64.86FEXPA expects i in bits 5:0 of the input so it can be used as index into87FEXPA hardwired table T[i] = 2^(i/64) for i = 0:63, that will in turn88populate the mantissa of the output. Therefore, we use u=asuint(z) as89input to FEXPA.9091We add 1023 to the modified shift value in order to set bits 16:6 of u to921, such that once these bits are moved to the exponent of the output of93FEXPA, we get the exponent of 2^n right, i.e. we get 2^m. */94svfloat64_t z = svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2);95svuint64_t u = svreinterpret_u64 (z);96svfloat64_t n = svsub_x (pg, z, d->shift);97svfloat64_t c13 = svld1rq (svptrue_b64 (), &d->c1);98/* r = x - n * ln2, r is in [-ln2/(2N), ln2/(2N)]. */99svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi);100svfloat64_t r = svmls_lane (x, n, ln2, 0);101r = svmls_lane (r, n, ln2, 1);102103/* y = exp(r) - 1 ~= r + C0 r^2 + C1 r^3 + C2 r^4 + C3 r^5. */104svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r);105svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), r, c13, 0);106svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), r, c13, 1);107svfloat64_t p04 = svmla_x (pg, p01, p23, r2);108svfloat64_t y = svmla_x (pg, r, p04, r2);109110/* s = 2^n, computed using FEXPA. FEXPA does not propagate NaNs, so for111consistent NaN handling we have to manually propagate them. This comes at112significant performance cost. */113svfloat64_t s = svexpa (u);114115/* Assemble result as exp(x) = 2^n * exp(r). If |x| > Thresh the116multiplication may overflow, so use special case routine. */117118if (unlikely (svptest_any (pg, special)))119{120/* FEXPA zeroes the sign bit, however the sign is meaningful to the121special case function so needs to be copied.122e = sign bit of u << 46. */123svuint64_t e = svand_x (pg, svlsl_x (pg, u, 46), 0x8000000000000000);124/* Copy sign to s. */125s = svreinterpret_f64 (svadd_x (pg, e, svreinterpret_u64 (s)));126return special_case (pg, s, y, n);127}128129/* No special case. */130return svmla_x (pg, s, s, y);131}132133TEST_SIG (SV, D, 1, exp, -9.9, 9.9)134TEST_ULP (SV_NAME_D1 (exp), 1.46)135TEST_DISABLE_FENV (SV_NAME_D1 (exp))136TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 0, 0x1p-23, 40000)137TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 0x1p-23, 1, 50000)138TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 1, 0x1p23, 50000)139TEST_SYM_INTERVAL (SV_NAME_D1 (exp), 0x1p23, inf, 50000)140CLOSE_SVE_ATTR141142143