Path: blob/main/contrib/arm-optimized-routines/math/aarch64/experimental/log1pf_2u1.c
48375 views
/*1* Single-precision log(1+x) function.2*3* Copyright (c) 2022-2024, Arm Limited.4* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception5*/67#include "poly_scalar_f32.h"8#include "math_config.h"9#include "test_sig.h"10#include "test_defs.h"1112#define Ln2 (0x1.62e43p-1f)13#define SignMask (0x80000000)1415/* Biased exponent of the largest float m for which m^8 underflows. */16#define M8UFLOW_BOUND_BEXP 11217/* Biased exponent of the largest float for which we just return x. */18#define TINY_BOUND_BEXP 1031920#define C(i) __log1pf_data.coeffs[i]2122static inline float23eval_poly (float m, uint32_t e)24{25#ifdef LOG1PF_2U52627/* 2.5 ulp variant. Approximate log(1+m) on [-0.25, 0.5] using28slightly modified Estrin scheme (no x^0 term, and x term is just x). */29float p_12 = fmaf (m, C (1), C (0));30float p_34 = fmaf (m, C (3), C (2));31float p_56 = fmaf (m, C (5), C (4));32float p_78 = fmaf (m, C (7), C (6));3334float m2 = m * m;35float p_02 = fmaf (m2, p_12, m);36float p_36 = fmaf (m2, p_56, p_34);37float p_79 = fmaf (m2, C (8), p_78);3839float m4 = m2 * m2;40float p_06 = fmaf (m4, p_36, p_02);4142if (unlikely (e < M8UFLOW_BOUND_BEXP))43return p_06;4445float m8 = m4 * m4;46return fmaf (m8, p_79, p_06);4748#elif defined(LOG1PF_1U3)4950/* 1.3 ulp variant. Approximate log(1+m) on [-0.25, 0.5] using Horner51scheme. Our polynomial approximation for log1p has the form52x + C1 * x^2 + C2 * x^3 + C3 * x^4 + ...53Hence approximation has the form m + m^2 * P(m)54where P(x) = C1 + C2 * x + C3 * x^2 + ... . */55return fmaf (m, m * horner_8_f32 (m, __log1pf_data.coeffs), m);5657#else58#error No log1pf approximation exists with the requested precision. Options are 13 or 25.59#endif60}6162static inline uint32_t63biased_exponent (uint32_t ix)64{65return (ix & 0x7f800000) >> 23;66}6768/* log1pf approximation using polynomial on reduced interval. Worst-case error69when using Estrin is roughly 2.02 ULP:70log1pf(0x1.21e13ap-2) got 0x1.fe8028p-3 want 0x1.fe802cp-3. */71float72log1pf (float x)73{74uint32_t ix = asuint (x);75uint32_t ia = ix & ~SignMask;76uint32_t ia12 = ia >> 20;77uint32_t e = biased_exponent (ix);7879/* Handle special cases first. */80if (unlikely (ia12 >= 0x7f8 || ix >= 0xbf800000 || ix == 0x8000000081|| e <= TINY_BOUND_BEXP))82{83if (ix == 0xff800000)84{85/* x == -Inf => log1pf(x) = NaN. */86return NAN;87}88if ((ix == 0x7f800000 || e <= TINY_BOUND_BEXP) && ia12 <= 0x7f8)89{90/* |x| < TinyBound => log1p(x) = x.91x == Inf => log1pf(x) = Inf. */92return x;93}94if (ix == 0xbf800000)95{96/* x == -1.0 => log1pf(x) = -Inf. */97return __math_divzerof (-1);98}99if (ia12 >= 0x7f8)100{101/* x == +/-NaN => log1pf(x) = NaN. */102return __math_invalidf (asfloat (ia));103}104/* x < -1.0 => log1pf(x) = NaN. */105return __math_invalidf (x);106}107108/* With x + 1 = t * 2^k (where t = m + 1 and k is chosen such that m109is in [-0.25, 0.5]):110log1p(x) = log(t) + log(2^k) = log1p(m) + k*log(2).111112We approximate log1p(m) with a polynomial, then scale by113k*log(2). Instead of doing this directly, we use an intermediate114scale factor s = 4*k*log(2) to ensure the scale is representable115as a normalised fp32 number. */116117if (ix <= 0x3f000000 || ia <= 0x3e800000)118{119/* If x is in [-0.25, 0.5] then we can shortcut all the logic120below, as k = 0 and m = x. All we need is to return the121polynomial. */122return eval_poly (x, e);123}124125float m = x + 1.0f;126127/* k is used scale the input. 0x3f400000 is chosen as we are trying to128reduce x to the range [-0.25, 0.5]. Inside this range, k is 0.129Outside this range, if k is reinterpreted as (NOT CONVERTED TO) float:130let k = sign * 2^p where sign = -1 if x < 01311 otherwise132and p is a negative integer whose magnitude increases with the133magnitude of x. */134int k = (asuint (m) - 0x3f400000) & 0xff800000;135136/* By using integer arithmetic, we obtain the necessary scaling by137subtracting the unbiased exponent of k from the exponent of x. */138float m_scale = asfloat (asuint (x) - k);139140/* Scale up to ensure that the scale factor is representable as normalised141fp32 number (s in [2**-126,2**26]), and scale m down accordingly. */142float s = asfloat (asuint (4.0f) - k);143m_scale = m_scale + fmaf (0.25f, s, -1.0f);144145float p = eval_poly (m_scale, biased_exponent (asuint (m_scale)));146147/* The scale factor to be applied back at the end - by multiplying float(k)148by 2^-23 we get the unbiased exponent of k. */149float scale_back = (float) k * 0x1.0p-23f;150151/* Apply the scaling back. */152return fmaf (scale_back, Ln2, p);153}154155TEST_SIG (S, F, 1, log1p, -0.9, 10.0)156TEST_ULP (log1pf, 1.52)157TEST_SYM_INTERVAL (log1pf, 0.0, 0x1p-23, 50000)158TEST_SYM_INTERVAL (log1pf, 0x1p-23, 0.001, 50000)159TEST_SYM_INTERVAL (log1pf, 0.001, 1.0, 50000)160TEST_SYM_INTERVAL (log1pf, 1.0, inf, 5000)161162163