1*f3087befSAndrew Turner /* 2*f3087befSAndrew Turner * Single-precision log(1+x) function. 3*f3087befSAndrew Turner * 4*f3087befSAndrew Turner * Copyright (c) 2022-2024, 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 "poly_scalar_f32.h" 9*f3087befSAndrew Turner #include "math_config.h" 10*f3087befSAndrew Turner #include "test_sig.h" 11*f3087befSAndrew Turner #include "test_defs.h" 12*f3087befSAndrew Turner 13*f3087befSAndrew Turner #define Ln2 (0x1.62e43p-1f) 14*f3087befSAndrew Turner #define SignMask (0x80000000) 15*f3087befSAndrew Turner 16*f3087befSAndrew Turner /* Biased exponent of the largest float m for which m^8 underflows. */ 17*f3087befSAndrew Turner #define M8UFLOW_BOUND_BEXP 112 18*f3087befSAndrew Turner /* Biased exponent of the largest float for which we just return x. */ 19*f3087befSAndrew Turner #define TINY_BOUND_BEXP 103 20*f3087befSAndrew Turner 21*f3087befSAndrew Turner #define C(i) __log1pf_data.coeffs[i] 22*f3087befSAndrew Turner 23*f3087befSAndrew Turner static inline float 24*f3087befSAndrew Turner eval_poly (float m, uint32_t e) 25*f3087befSAndrew Turner { 26*f3087befSAndrew Turner #ifdef LOG1PF_2U5 27*f3087befSAndrew Turner 28*f3087befSAndrew Turner /* 2.5 ulp variant. Approximate log(1+m) on [-0.25, 0.5] using 29*f3087befSAndrew Turner slightly modified Estrin scheme (no x^0 term, and x term is just x). */ 30*f3087befSAndrew Turner float p_12 = fmaf (m, C (1), C (0)); 31*f3087befSAndrew Turner float p_34 = fmaf (m, C (3), C (2)); 32*f3087befSAndrew Turner float p_56 = fmaf (m, C (5), C (4)); 33*f3087befSAndrew Turner float p_78 = fmaf (m, C (7), C (6)); 34*f3087befSAndrew Turner 35*f3087befSAndrew Turner float m2 = m * m; 36*f3087befSAndrew Turner float p_02 = fmaf (m2, p_12, m); 37*f3087befSAndrew Turner float p_36 = fmaf (m2, p_56, p_34); 38*f3087befSAndrew Turner float p_79 = fmaf (m2, C (8), p_78); 39*f3087befSAndrew Turner 40*f3087befSAndrew Turner float m4 = m2 * m2; 41*f3087befSAndrew Turner float p_06 = fmaf (m4, p_36, p_02); 42*f3087befSAndrew Turner 43*f3087befSAndrew Turner if (unlikely (e < M8UFLOW_BOUND_BEXP)) 44*f3087befSAndrew Turner return p_06; 45*f3087befSAndrew Turner 46*f3087befSAndrew Turner float m8 = m4 * m4; 47*f3087befSAndrew Turner return fmaf (m8, p_79, p_06); 48*f3087befSAndrew Turner 49*f3087befSAndrew Turner #elif defined(LOG1PF_1U3) 50*f3087befSAndrew Turner 51*f3087befSAndrew Turner /* 1.3 ulp variant. Approximate log(1+m) on [-0.25, 0.5] using Horner 52*f3087befSAndrew Turner scheme. Our polynomial approximation for log1p has the form 53*f3087befSAndrew Turner x + C1 * x^2 + C2 * x^3 + C3 * x^4 + ... 54*f3087befSAndrew Turner Hence approximation has the form m + m^2 * P(m) 55*f3087befSAndrew Turner where P(x) = C1 + C2 * x + C3 * x^2 + ... . */ 56*f3087befSAndrew Turner return fmaf (m, m * horner_8_f32 (m, __log1pf_data.coeffs), m); 57*f3087befSAndrew Turner 58*f3087befSAndrew Turner #else 59*f3087befSAndrew Turner #error No log1pf approximation exists with the requested precision. Options are 13 or 25. 60*f3087befSAndrew Turner #endif 61*f3087befSAndrew Turner } 62*f3087befSAndrew Turner 63*f3087befSAndrew Turner static inline uint32_t 64*f3087befSAndrew Turner biased_exponent (uint32_t ix) 65*f3087befSAndrew Turner { 66*f3087befSAndrew Turner return (ix & 0x7f800000) >> 23; 67*f3087befSAndrew Turner } 68*f3087befSAndrew Turner 69*f3087befSAndrew Turner /* log1pf approximation using polynomial on reduced interval. Worst-case error 70*f3087befSAndrew Turner when using Estrin is roughly 2.02 ULP: 71*f3087befSAndrew Turner log1pf(0x1.21e13ap-2) got 0x1.fe8028p-3 want 0x1.fe802cp-3. */ 72*f3087befSAndrew Turner float 73*f3087befSAndrew Turner log1pf (float x) 74*f3087befSAndrew Turner { 75*f3087befSAndrew Turner uint32_t ix = asuint (x); 76*f3087befSAndrew Turner uint32_t ia = ix & ~SignMask; 77*f3087befSAndrew Turner uint32_t ia12 = ia >> 20; 78*f3087befSAndrew Turner uint32_t e = biased_exponent (ix); 79*f3087befSAndrew Turner 80*f3087befSAndrew Turner /* Handle special cases first. */ 81*f3087befSAndrew Turner if (unlikely (ia12 >= 0x7f8 || ix >= 0xbf800000 || ix == 0x80000000 82*f3087befSAndrew Turner || e <= TINY_BOUND_BEXP)) 83*f3087befSAndrew Turner { 84*f3087befSAndrew Turner if (ix == 0xff800000) 85*f3087befSAndrew Turner { 86*f3087befSAndrew Turner /* x == -Inf => log1pf(x) = NaN. */ 87*f3087befSAndrew Turner return NAN; 88*f3087befSAndrew Turner } 89*f3087befSAndrew Turner if ((ix == 0x7f800000 || e <= TINY_BOUND_BEXP) && ia12 <= 0x7f8) 90*f3087befSAndrew Turner { 91*f3087befSAndrew Turner /* |x| < TinyBound => log1p(x) = x. 92*f3087befSAndrew Turner x == Inf => log1pf(x) = Inf. */ 93*f3087befSAndrew Turner return x; 94*f3087befSAndrew Turner } 95*f3087befSAndrew Turner if (ix == 0xbf800000) 96*f3087befSAndrew Turner { 97*f3087befSAndrew Turner /* x == -1.0 => log1pf(x) = -Inf. */ 98*f3087befSAndrew Turner return __math_divzerof (-1); 99*f3087befSAndrew Turner } 100*f3087befSAndrew Turner if (ia12 >= 0x7f8) 101*f3087befSAndrew Turner { 102*f3087befSAndrew Turner /* x == +/-NaN => log1pf(x) = NaN. */ 103*f3087befSAndrew Turner return __math_invalidf (asfloat (ia)); 104*f3087befSAndrew Turner } 105*f3087befSAndrew Turner /* x < -1.0 => log1pf(x) = NaN. */ 106*f3087befSAndrew Turner return __math_invalidf (x); 107*f3087befSAndrew Turner } 108*f3087befSAndrew Turner 109*f3087befSAndrew Turner /* With x + 1 = t * 2^k (where t = m + 1 and k is chosen such that m 110*f3087befSAndrew Turner is in [-0.25, 0.5]): 111*f3087befSAndrew Turner log1p(x) = log(t) + log(2^k) = log1p(m) + k*log(2). 112*f3087befSAndrew Turner 113*f3087befSAndrew Turner We approximate log1p(m) with a polynomial, then scale by 114*f3087befSAndrew Turner k*log(2). Instead of doing this directly, we use an intermediate 115*f3087befSAndrew Turner scale factor s = 4*k*log(2) to ensure the scale is representable 116*f3087befSAndrew Turner as a normalised fp32 number. */ 117*f3087befSAndrew Turner 118*f3087befSAndrew Turner if (ix <= 0x3f000000 || ia <= 0x3e800000) 119*f3087befSAndrew Turner { 120*f3087befSAndrew Turner /* If x is in [-0.25, 0.5] then we can shortcut all the logic 121*f3087befSAndrew Turner below, as k = 0 and m = x. All we need is to return the 122*f3087befSAndrew Turner polynomial. */ 123*f3087befSAndrew Turner return eval_poly (x, e); 124*f3087befSAndrew Turner } 125*f3087befSAndrew Turner 126*f3087befSAndrew Turner float m = x + 1.0f; 127*f3087befSAndrew Turner 128*f3087befSAndrew Turner /* k is used scale the input. 0x3f400000 is chosen as we are trying to 129*f3087befSAndrew Turner reduce x to the range [-0.25, 0.5]. Inside this range, k is 0. 130*f3087befSAndrew Turner Outside this range, if k is reinterpreted as (NOT CONVERTED TO) float: 131*f3087befSAndrew Turner let k = sign * 2^p where sign = -1 if x < 0 132*f3087befSAndrew Turner 1 otherwise 133*f3087befSAndrew Turner and p is a negative integer whose magnitude increases with the 134*f3087befSAndrew Turner magnitude of x. */ 135*f3087befSAndrew Turner int k = (asuint (m) - 0x3f400000) & 0xff800000; 136*f3087befSAndrew Turner 137*f3087befSAndrew Turner /* By using integer arithmetic, we obtain the necessary scaling by 138*f3087befSAndrew Turner subtracting the unbiased exponent of k from the exponent of x. */ 139*f3087befSAndrew Turner float m_scale = asfloat (asuint (x) - k); 140*f3087befSAndrew Turner 141*f3087befSAndrew Turner /* Scale up to ensure that the scale factor is representable as normalised 142*f3087befSAndrew Turner fp32 number (s in [2**-126,2**26]), and scale m down accordingly. */ 143*f3087befSAndrew Turner float s = asfloat (asuint (4.0f) - k); 144*f3087befSAndrew Turner m_scale = m_scale + fmaf (0.25f, s, -1.0f); 145*f3087befSAndrew Turner 146*f3087befSAndrew Turner float p = eval_poly (m_scale, biased_exponent (asuint (m_scale))); 147*f3087befSAndrew Turner 148*f3087befSAndrew Turner /* The scale factor to be applied back at the end - by multiplying float(k) 149*f3087befSAndrew Turner by 2^-23 we get the unbiased exponent of k. */ 150*f3087befSAndrew Turner float scale_back = (float) k * 0x1.0p-23f; 151*f3087befSAndrew Turner 152*f3087befSAndrew Turner /* Apply the scaling back. */ 153*f3087befSAndrew Turner return fmaf (scale_back, Ln2, p); 154*f3087befSAndrew Turner } 155*f3087befSAndrew Turner 156*f3087befSAndrew Turner TEST_SIG (S, F, 1, log1p, -0.9, 10.0) 157*f3087befSAndrew Turner TEST_ULP (log1pf, 1.52) 158*f3087befSAndrew Turner TEST_SYM_INTERVAL (log1pf, 0.0, 0x1p-23, 50000) 159*f3087befSAndrew Turner TEST_SYM_INTERVAL (log1pf, 0x1p-23, 0.001, 50000) 160*f3087befSAndrew Turner TEST_SYM_INTERVAL (log1pf, 0.001, 1.0, 50000) 161*f3087befSAndrew Turner TEST_SYM_INTERVAL (log1pf, 1.0, inf, 5000) 162