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