Path: blob/main/contrib/arm-optimized-routines/math/aarch64/sve/powf.c
48375 views
/*1* Single-precision SVE powf 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"1011/* The following data is used in the SVE pow core computation12and special case detection. */13#define Tinvc __v_powf_data.invc14#define Tlogc __v_powf_data.logc15#define Texp __v_powf_data.scale16#define SignBias (1 << (V_POWF_EXP2_TABLE_BITS + 11))17#define Norm 0x1p23f /* 0x4b000000. */1819/* Overall ULP error bound for pow is 2.6 ulp20~ 0.5 + 2^24 (128*Ln2*relerr_log2 + relerr_exp2). */21static const struct data22{23double log_poly[4];24double exp_poly[3];25float uflow_bound, oflow_bound, small_bound;26uint32_t sign_bias, subnormal_bias, off;27} data = {28/* rel err: 1.5 * 2^-30. Each coefficients is multiplied the value of29V_POWF_EXP2_N. */30.log_poly = { -0x1.6ff5daa3b3d7cp+3, 0x1.ec81d03c01aebp+3,31-0x1.71547bb43f101p+4, 0x1.7154764a815cbp+5 },32/* rel err: 1.69 * 2^-34. */33.exp_poly = {340x1.c6af84b912394p-20, /* A0 / V_POWF_EXP2_N^3. */350x1.ebfce50fac4f3p-13, /* A1 / V_POWF_EXP2_N^2. */360x1.62e42ff0c52d6p-6, /* A3 / V_POWF_EXP2_N. */37},38.uflow_bound = -0x1.2cp+12f, /* -150.0 * V_POWF_EXP2_N. */39.oflow_bound = 0x1p+12f, /* 128.0 * V_POWF_EXP2_N. */40.small_bound = 0x1p-126f,41.off = 0x3f35d000,42.sign_bias = SignBias,43.subnormal_bias = 0x0b800000, /* 23 << 23. */44};4546#define A(i) sv_f64 (d->log_poly[i])47#define C(i) sv_f64 (d->exp_poly[i])4849/* Check if x is an integer. */50static inline svbool_t51svisint (svbool_t pg, svfloat32_t x)52{53return svcmpeq (pg, svrintz_z (pg, x), x);54}5556/* Check if x is real not integer valued. */57static inline svbool_t58svisnotint (svbool_t pg, svfloat32_t x)59{60return svcmpne (pg, svrintz_z (pg, x), x);61}6263/* Check if x is an odd integer. */64static inline svbool_t65svisodd (svbool_t pg, svfloat32_t x)66{67svfloat32_t y = svmul_x (pg, x, 0.5f);68return svisnotint (pg, y);69}7071/* Check if zero, inf or nan. */72static inline svbool_t73sv_zeroinfnan (svbool_t pg, svuint32_t i)74{75return svcmpge (pg, svsub_x (pg, svadd_x (pg, i, i), 1),762u * 0x7f800000 - 1);77}7879/* Returns 0 if not int, 1 if odd int, 2 if even int. The argument is80the bit representation of a non-zero finite floating-point value. */81static inline int82checkint (uint32_t iy)83{84int e = iy >> 23 & 0xff;85if (e < 0x7f)86return 0;87if (e > 0x7f + 23)88return 2;89if (iy & ((1 << (0x7f + 23 - e)) - 1))90return 0;91if (iy & (1 << (0x7f + 23 - e)))92return 1;93return 2;94}9596/* Check if zero, inf or nan. */97static inline int98zeroinfnan (uint32_t ix)99{100return 2 * ix - 1 >= 2u * 0x7f800000 - 1;101}102103/* A scalar subroutine used to fix main power special cases. Similar to the104preamble of scalar powf except that we do not update ix and sign_bias. This105is done in the preamble of the SVE powf. */106static inline float107powf_specialcase (float x, float y, float z)108{109uint32_t ix = asuint (x);110uint32_t iy = asuint (y);111/* Either (x < 0x1p-126 or inf or nan) or (y is 0 or inf or nan). */112if (unlikely (zeroinfnan (iy)))113{114if (2 * iy == 0)115return issignalingf_inline (x) ? x + y : 1.0f;116if (ix == 0x3f800000)117return issignalingf_inline (y) ? x + y : 1.0f;118if (2 * ix > 2u * 0x7f800000 || 2 * iy > 2u * 0x7f800000)119return x + y;120if (2 * ix == 2 * 0x3f800000)121return 1.0f;122if ((2 * ix < 2 * 0x3f800000) == !(iy & 0x80000000))123return 0.0f; /* |x|<1 && y==inf or |x|>1 && y==-inf. */124return y * y;125}126if (unlikely (zeroinfnan (ix)))127{128float_t x2 = x * x;129if (ix & 0x80000000 && checkint (iy) == 1)130x2 = -x2;131return iy & 0x80000000 ? 1 / x2 : x2;132}133/* We need a return here in case x<0 and y is integer, but all other tests134need to be run. */135return z;136}137138/* Scalar fallback for special case routines with custom signature. */139static svfloat32_t NOINLINE140sv_call_powf_sc (svfloat32_t x1, svfloat32_t x2, svfloat32_t y)141{142/* Special cases of x or y: zero, inf and nan. */143svbool_t xspecial = sv_zeroinfnan (svptrue_b32 (), svreinterpret_u32 (x1));144svbool_t yspecial = sv_zeroinfnan (svptrue_b32 (), svreinterpret_u32 (x2));145svbool_t cmp = svorr_z (svptrue_b32 (), xspecial, yspecial);146147svbool_t p = svpfirst (cmp, svpfalse ());148while (svptest_any (cmp, p))149{150float sx1 = svclastb (p, 0, x1);151float sx2 = svclastb (p, 0, x2);152float elem = svclastb (p, 0, y);153elem = powf_specialcase (sx1, sx2, elem);154svfloat32_t y2 = sv_f32 (elem);155y = svsel (p, y2, y);156p = svpnext_b32 (cmp, p);157}158return y;159}160161/* Compute core for half of the lanes in double precision. */162static inline svfloat64_t163sv_powf_core_ext (const svbool_t pg, svuint64_t i, svfloat64_t z, svint64_t k,164svfloat64_t y, svuint64_t sign_bias, svfloat64_t *pylogx,165const struct data *d)166{167svfloat64_t invc = svld1_gather_index (pg, Tinvc, i);168svfloat64_t logc = svld1_gather_index (pg, Tlogc, i);169170/* log2(x) = log1p(z/c-1)/ln2 + log2(c) + k. */171svfloat64_t r = svmla_x (pg, sv_f64 (-1.0), z, invc);172svfloat64_t y0 = svadd_x (pg, logc, svcvt_f64_x (pg, k));173174/* Polynomial to approximate log1p(r)/ln2. */175svfloat64_t logx = A (0);176logx = svmad_x (pg, r, logx, A (1));177logx = svmad_x (pg, r, logx, A (2));178logx = svmad_x (pg, r, logx, A (3));179logx = svmad_x (pg, r, logx, y0);180*pylogx = svmul_x (pg, y, logx);181182/* z - kd is in [-1, 1] in non-nearest rounding modes. */183svfloat64_t kd = svrinta_x (svptrue_b64 (), *pylogx);184svuint64_t ki = svreinterpret_u64 (svcvt_s64_x (svptrue_b64 (), kd));185186r = svsub_x (pg, *pylogx, kd);187188/* exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1). */189svuint64_t t = svld1_gather_index (190svptrue_b64 (), Texp, svand_x (svptrue_b64 (), ki, V_POWF_EXP2_N - 1));191svuint64_t ski = svadd_x (svptrue_b64 (), ki, sign_bias);192t = svadd_x (svptrue_b64 (), t,193svlsl_x (svptrue_b64 (), ski, 52 - V_POWF_EXP2_TABLE_BITS));194svfloat64_t s = svreinterpret_f64 (t);195196svfloat64_t p = C (0);197p = svmla_x (pg, C (1), p, r);198p = svmla_x (pg, C (2), p, r);199p = svmla_x (pg, s, p, svmul_x (svptrue_b64 (), s, r));200201return p;202}203204/* Widen vector to double precision and compute core on both halves of the205vector. Lower cost of promotion by considering all lanes active. */206static inline svfloat32_t207sv_powf_core (const svbool_t pg, svuint32_t i, svuint32_t iz, svint32_t k,208svfloat32_t y, svuint32_t sign_bias, svfloat32_t *pylogx,209const struct data *d)210{211const svbool_t ptrue = svptrue_b64 ();212213/* Unpack and promote input vectors (pg, y, z, i, k and sign_bias) into two214in order to perform core computation in double precision. */215const svbool_t pg_lo = svunpklo (pg);216const svbool_t pg_hi = svunpkhi (pg);217svfloat64_t y_lo218= svcvt_f64_x (pg, svreinterpret_f32 (svunpklo (svreinterpret_u32 (y))));219svfloat64_t y_hi220= svcvt_f64_x (pg, svreinterpret_f32 (svunpkhi (svreinterpret_u32 (y))));221svfloat64_t z_lo = svcvt_f64_x (pg, svreinterpret_f32 (svunpklo (iz)));222svfloat64_t z_hi = svcvt_f64_x (pg, svreinterpret_f32 (svunpkhi (iz)));223svuint64_t i_lo = svunpklo (i);224svuint64_t i_hi = svunpkhi (i);225svint64_t k_lo = svunpklo (k);226svint64_t k_hi = svunpkhi (k);227svuint64_t sign_bias_lo = svunpklo (sign_bias);228svuint64_t sign_bias_hi = svunpkhi (sign_bias);229230/* Compute each part in double precision. */231svfloat64_t ylogx_lo, ylogx_hi;232svfloat64_t lo = sv_powf_core_ext (pg_lo, i_lo, z_lo, k_lo, y_lo,233sign_bias_lo, &ylogx_lo, d);234svfloat64_t hi = sv_powf_core_ext (pg_hi, i_hi, z_hi, k_hi, y_hi,235sign_bias_hi, &ylogx_hi, d);236237/* Convert back to single-precision and interleave. */238svfloat32_t ylogx_lo_32 = svcvt_f32_x (ptrue, ylogx_lo);239svfloat32_t ylogx_hi_32 = svcvt_f32_x (ptrue, ylogx_hi);240*pylogx = svuzp1 (ylogx_lo_32, ylogx_hi_32);241svfloat32_t lo_32 = svcvt_f32_x (ptrue, lo);242svfloat32_t hi_32 = svcvt_f32_x (ptrue, hi);243return svuzp1 (lo_32, hi_32);244}245246/* Implementation of SVE powf.247Provides the same accuracy as AdvSIMD powf, since it relies on the same248algorithm. The theoretical maximum error is under 2.60 ULPs.249Maximum measured error is 2.57 ULPs:250SV_NAME_F2 (pow) (0x1.031706p+0, 0x1.ce2ec2p+12) got 0x1.fff868p+127251want 0x1.fff862p+127. */252svfloat32_t SV_NAME_F2 (pow) (svfloat32_t x, svfloat32_t y, const svbool_t pg)253{254const struct data *d = ptr_barrier (&data);255256svuint32_t vix0 = svreinterpret_u32 (x);257svuint32_t viy0 = svreinterpret_u32 (y);258259/* Negative x cases. */260svbool_t xisneg = svcmplt (pg, x, sv_f32 (0));261262/* Set sign_bias and ix depending on sign of x and nature of y. */263svbool_t yint_or_xpos = pg;264svuint32_t sign_bias = sv_u32 (0);265svuint32_t vix = vix0;266if (unlikely (svptest_any (pg, xisneg)))267{268/* Determine nature of y. */269yint_or_xpos = svisint (xisneg, y);270svbool_t yisodd_xisneg = svisodd (xisneg, y);271/* ix set to abs(ix) if y is integer. */272vix = svand_m (yint_or_xpos, vix0, 0x7fffffff);273/* Set to SignBias if x is negative and y is odd. */274sign_bias = svsel (yisodd_xisneg, sv_u32 (d->sign_bias), sv_u32 (0));275}276277/* Special cases of x or y: zero, inf and nan. */278svbool_t xspecial = sv_zeroinfnan (pg, vix0);279svbool_t yspecial = sv_zeroinfnan (pg, viy0);280svbool_t cmp = svorr_z (pg, xspecial, yspecial);281282/* Small cases of x: |x| < 0x1p-126. */283svbool_t xsmall = svaclt (yint_or_xpos, x, d->small_bound);284if (unlikely (svptest_any (yint_or_xpos, xsmall)))285{286/* Normalize subnormal x so exponent becomes negative. */287svuint32_t vix_norm = svreinterpret_u32 (svmul_x (xsmall, x, Norm));288vix_norm = svand_x (xsmall, vix_norm, 0x7fffffff);289vix_norm = svsub_x (xsmall, vix_norm, d->subnormal_bias);290vix = svsel (xsmall, vix_norm, vix);291}292/* Part of core computation carried in working precision. */293svuint32_t tmp = svsub_x (yint_or_xpos, vix, d->off);294svuint32_t i = svand_x (295yint_or_xpos, svlsr_x (yint_or_xpos, tmp, (23 - V_POWF_LOG2_TABLE_BITS)),296V_POWF_LOG2_N - 1);297svuint32_t top = svand_x (yint_or_xpos, tmp, 0xff800000);298svuint32_t iz = svsub_x (yint_or_xpos, vix, top);299svint32_t k = svasr_x (yint_or_xpos, svreinterpret_s32 (top),300(23 - V_POWF_EXP2_TABLE_BITS));301302/* Compute core in extended precision and return intermediate ylogx results303to handle cases of underflow and underflow in exp. */304svfloat32_t ylogx;305svfloat32_t ret306= sv_powf_core (yint_or_xpos, i, iz, k, y, sign_bias, &ylogx, d);307308/* Handle exp special cases of underflow and overflow. */309svuint32_t sign310= svlsl_x (yint_or_xpos, sign_bias, 20 - V_POWF_EXP2_TABLE_BITS);311svfloat32_t ret_oflow312= svreinterpret_f32 (svorr_x (yint_or_xpos, sign, asuint (INFINITY)));313svfloat32_t ret_uflow = svreinterpret_f32 (sign);314ret = svsel (svcmple (yint_or_xpos, ylogx, d->uflow_bound), ret_uflow, ret);315ret = svsel (svcmpgt (yint_or_xpos, ylogx, d->oflow_bound), ret_oflow, ret);316317/* Cases of finite y and finite negative x. */318ret = svsel (yint_or_xpos, ret, sv_f32 (__builtin_nanf ("")));319320if (unlikely (svptest_any (cmp, cmp)))321return sv_call_powf_sc (x, y, ret);322323return ret;324}325326TEST_SIG (SV, F, 2, pow)327TEST_ULP (SV_NAME_F2 (pow), 2.08)328TEST_DISABLE_FENV (SV_NAME_F2 (pow))329/* Wide intervals spanning the whole domain but shared between x and y. */330#define SV_POWF_INTERVAL2(xlo, xhi, ylo, yhi, n) \331TEST_INTERVAL2 (SV_NAME_F2 (pow), xlo, xhi, ylo, yhi, n) \332TEST_INTERVAL2 (SV_NAME_F2 (pow), xlo, xhi, -ylo, -yhi, n) \333TEST_INTERVAL2 (SV_NAME_F2 (pow), -xlo, -xhi, ylo, yhi, n) \334TEST_INTERVAL2 (SV_NAME_F2 (pow), -xlo, -xhi, -ylo, -yhi, n)335SV_POWF_INTERVAL2 (0, 0x1p-126, 0, inf, 40000)336SV_POWF_INTERVAL2 (0x1p-126, 1, 0, inf, 50000)337SV_POWF_INTERVAL2 (1, inf, 0, inf, 50000)338/* x~1 or y~1. */339SV_POWF_INTERVAL2 (0x1p-1, 0x1p1, 0x1p-10, 0x1p10, 10000)340SV_POWF_INTERVAL2 (0x1.ep-1, 0x1.1p0, 0x1p8, 0x1p16, 10000)341SV_POWF_INTERVAL2 (0x1p-500, 0x1p500, 0x1p-1, 0x1p1, 10000)342/* around estimated argmaxs of ULP error. */343SV_POWF_INTERVAL2 (0x1p-300, 0x1p-200, 0x1p-20, 0x1p-10, 10000)344SV_POWF_INTERVAL2 (0x1p50, 0x1p100, 0x1p-20, 0x1p-10, 10000)345/* x is negative, y is odd or even integer, or y is real not integer. */346TEST_INTERVAL2 (SV_NAME_F2 (pow), -0.0, -10.0, 3.0, 3.0, 10000)347TEST_INTERVAL2 (SV_NAME_F2 (pow), -0.0, -10.0, 4.0, 4.0, 10000)348TEST_INTERVAL2 (SV_NAME_F2 (pow), -0.0, -10.0, 0.0, 10.0, 10000)349TEST_INTERVAL2 (SV_NAME_F2 (pow), 0.0, 10.0, -0.0, -10.0, 10000)350/* |x| is inf, y is odd or even integer, or y is real not integer. */351SV_POWF_INTERVAL2 (inf, inf, 0.5, 0.5, 1)352SV_POWF_INTERVAL2 (inf, inf, 1.0, 1.0, 1)353SV_POWF_INTERVAL2 (inf, inf, 2.0, 2.0, 1)354SV_POWF_INTERVAL2 (inf, inf, 3.0, 3.0, 1)355/* 0.0^y. */356SV_POWF_INTERVAL2 (0.0, 0.0, 0.0, 0x1p120, 1000)357/* 1.0^y. */358TEST_INTERVAL2 (SV_NAME_F2 (pow), 1.0, 1.0, 0.0, 0x1p-50, 1000)359TEST_INTERVAL2 (SV_NAME_F2 (pow), 1.0, 1.0, 0x1p-50, 1.0, 1000)360TEST_INTERVAL2 (SV_NAME_F2 (pow), 1.0, 1.0, 1.0, 0x1p100, 1000)361TEST_INTERVAL2 (SV_NAME_F2 (pow), 1.0, 1.0, -1.0, -0x1p120, 1000)362CLOSE_SVE_ATTR363364365