-/* origin: FreeBSD /usr/src/lib/msun/src/e_expf.c */
/*
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
- */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ * Single-precision e^x function.
*
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
+ * Copyright (c) 2017-2018, Arm Limited.
+ * SPDX-License-Identifier: MIT
*/
+#include <math.h>
+#include <stdint.h>
#include "libm.h"
+#include "exp2f_data.h"
-static const float
-one = 1.0,
-halF[2] = {0.5,-0.5,},
-huge = 1.0e+30,
-o_threshold = 8.8721679688e+01, /* 0x42b17180 */
-u_threshold = -1.0397208405e+02, /* 0xc2cff1b5 */
-ln2HI[2] = { 6.9314575195e-01, /* 0x3f317200 */
- -6.9314575195e-01,},/* 0xbf317200 */
-ln2LO[2] = { 1.4286067653e-06, /* 0x35bfbe8e */
- -1.4286067653e-06,},/* 0xb5bfbe8e */
-invln2 = 1.4426950216e+00, /* 0x3fb8aa3b */
/*
- * Domain [-0.34568, 0.34568], range ~[-4.278e-9, 4.447e-9]:
- * |x*(exp(x)+1)/(exp(x)-1) - p(x)| < 2**-27.74
- */
-P1 = 1.6666625440e-1, /* 0xaaaa8f.0p-26 */
-P2 = -2.7667332906e-3; /* -0xb55215.0p-32 */
+EXP2F_TABLE_BITS = 5
+EXP2F_POLY_ORDER = 3
-static volatile float twom100 = 7.8886090522e-31; /* 2**-100=0x0d800000 */
+ULP error: 0.502 (nearest rounding.)
+Relative error: 1.69 * 2^-34 in [-ln2/64, ln2/64] (before rounding.)
+Wrong count: 170635 (all nearest rounding wrong results with fma.)
+Non-nearest ULP error: 1 (rounded ULP error)
+*/
-float expf(float x)
+#define N (1 << EXP2F_TABLE_BITS)
+#define InvLn2N __exp2f_data.invln2_scaled
+#define T __exp2f_data.tab
+#define C __exp2f_data.poly_scaled
+
+static inline uint32_t top12(float x)
{
- float y,hi=0.0,lo=0.0,c,t,twopk;
- int32_t k=0,xsb;
- uint32_t hx;
+ return asuint(x) >> 20;
+}
- GET_FLOAT_WORD(hx, x);
- xsb = (hx>>31)&1; /* sign bit of x */
- hx &= 0x7fffffff; /* high word of |x| */
+float expf(float x)
+{
+ uint32_t abstop;
+ uint64_t ki, t;
+ double_t kd, xd, z, r, r2, y, s;
- /* filter out non-finite argument */
- if (hx >= 0x42b17218) { /* if |x|>=88.721... */
- if (hx > 0x7f800000) /* NaN */
- return x+x;
- if (hx == 0x7f800000) /* exp(+-inf)={inf,0} */
- return xsb==0 ? x : 0.0;
- if (x > o_threshold)
- return huge*huge; /* overflow */
- if (x < u_threshold)
- return twom100*twom100; /* underflow */
+ xd = (double_t)x;
+ abstop = top12(x) & 0x7ff;
+ if (predict_false(abstop >= top12(88.0f))) {
+ /* |x| >= 88 or x is nan. */
+ if (asuint(x) == asuint(-INFINITY))
+ return 0.0f;
+ if (abstop >= top12(INFINITY))
+ return x + x;
+ if (x > 0x1.62e42ep6f) /* x > log(0x1p128) ~= 88.72 */
+ return __math_oflowf(0);
+ if (x < -0x1.9fe368p6f) /* x < log(0x1p-150) ~= -103.97 */
+ return __math_uflowf(0);
}
- /* argument reduction */
- if (hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */
- if (hx < 0x3F851592) { /* and |x| < 1.5 ln2 */
- hi = x-ln2HI[xsb];
- lo = ln2LO[xsb];
- k = 1 - xsb - xsb;
- } else {
- k = invln2*x + halF[xsb];
- t = k;
- hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */
- lo = t*ln2LO[0];
- }
- STRICT_ASSIGN(float, x, hi - lo);
- } else if(hx < 0x39000000) { /* |x|<2**-14 */
- /* raise inexact */
- if (huge+x > one)
- return one + x;
- } else
- k = 0;
+ /* x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k. */
+ z = InvLn2N * xd;
+
+ /* Round and convert z to int, the result is in [-150*N, 128*N] and
+ ideally ties-to-even rule is used, otherwise the magnitude of r
+ can be bigger which gives larger approximation error. */
+#if TOINT_INTRINSICS
+ kd = roundtoint(z);
+ ki = converttoint(z);
+#else
+# define SHIFT __exp2f_data.shift
+ kd = eval_as_double(z + SHIFT);
+ ki = asuint64(kd);
+ kd -= SHIFT;
+#endif
+ r = z - kd;
- /* x is now in primary range */
- t = x*x;
- if (k >= -125)
- SET_FLOAT_WORD(twopk, 0x3f800000+(k<<23));
- else
- SET_FLOAT_WORD(twopk, 0x3f800000+((k+100)<<23));
- c = x - t*(P1+t*P2);
- if (k == 0)
- return one - ((x*c)/(c-(float)2.0)-x);
- y = one - ((lo-(x*c)/((float)2.0-c))-hi);
- if (k < -125)
- return y*twopk*twom100;
- if (k == 128)
- return y*2.0F*0x1p127F;
- return y*twopk;
+ /* exp(x) = 2^(k/N) * 2^(r/N) ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) */
+ t = T[ki % N];
+ t += ki << (52 - EXP2F_TABLE_BITS);
+ s = asdouble(t);
+ z = C[0] * r + C[1];
+ r2 = r * r;
+ y = C[2] * r + 1;
+ y = z * r2 + y;
+ y = y * s;
+ return eval_as_float(y);
}