remove LFS64 symbol aliases; replace with dynamic linker remapping
[musl] / src / math / expf.c
index 16e9afe..f9fbf8e 100644 (file)
@@ -1,81 +1,80 @@
-/* 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
-half[2] = {0.5,-0.5},
-ln2hi   = 6.9314575195e-1f,  /* 0x3f317200 */
-ln2lo   = 1.4286067653e-6f,  /* 0x35bfbe8e */
-invln2  = 1.4426950216e+0f,  /* 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-1f, /*  0xaaaa8f.0p-26 */
-P2 = -2.7667332906e-3f; /* -0xb55215.0p-32 */
+EXP2F_TABLE_BITS = 5
+EXP2F_POLY_ORDER = 3
 
-float expf(float x)
+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)
+*/
+
+#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_t hi, lo, c, xx, y;
-       int k, sign;
-       uint32_t hx;
+       return asuint(x) >> 20;
+}
 
-       GET_FLOAT_WORD(hx, x);
-       sign = hx >> 31;   /* 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;
 
-       /* special cases */
-       if (hx >= 0x42aeac50) {  /* if |x| >= -87.33655f or NaN */
-               if (hx >= 0x42b17218 && !sign) {  /* x >= 88.722839f */
-                       /* overflow */
-                       x *= 0x1p127f;
-                       return x;
-               }
-               if (sign) {
-                       /* underflow */
-                       FORCE_EVAL(-0x1p-149f/x);
-                       if (hx >= 0x42cff1b5)  /* x <= -103.972084f */
-                               return 0;
-               }
+       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)  /* if |x| > 1.5 ln2 */
-                       k = invln2*x + half[sign];
-               else
-                       k = 1 - sign - sign;
-               hi = x - k*ln2hi;  /* k*ln2hi is exact here */
-               lo = k*ln2lo;
-               x = hi - lo;
-       } else if (hx > 0x39000000) {  /* |x| > 2**-14 */
-               k = 0;
-               hi = x;
-               lo = 0;
-       } else {
-               /* raise inexact */
-               FORCE_EVAL(0x1p127f + x);
-               return 1 + x;
-       }
+       /* 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 */
-       xx = x*x;
-       c = x - xx*(P1+xx*P2);
-       y = 1 + (x*c/(2-c) - lo + hi);
-       if (k == 0)
-               return y;
-       return scalbnf(y, k);
+       /* 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);
 }