Piotr Caban : msvcrt: Import powf implementation from musl.

Alexandre Julliard julliard at winehq.org
Thu Jun 10 16:04:51 CDT 2021


Module: wine
Branch: master
Commit: 7bb574eafd3a3ca61ced2f22e69a96b6bf848193
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=7bb574eafd3a3ca61ced2f22e69a96b6bf848193

Author: Piotr Caban <piotr at codeweavers.com>
Date:   Thu Jun 10 19:04:44 2021 +0200

msvcrt: Import powf implementation from musl.

Signed-off-by: Piotr Caban <piotr at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/msvcrt/math.c    | 177 ++++++++++++++++++++++++++++++++++++++++++++++++--
 dlls/msvcrt/unixlib.c |   9 ---
 dlls/msvcrt/unixlib.h |   1 -
 3 files changed, 171 insertions(+), 16 deletions(-)

diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index eaaeb9bec12..04597744548 100644
--- a/dlls/msvcrt/math.c
+++ b/dlls/msvcrt/math.c
@@ -1424,17 +1424,182 @@ float CDECL log10f( float x )
     return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi;
 }
 
+/* Subnormal input is normalized so ix has negative biased exponent.
+   Output is multiplied by POWF_SCALE (where 1 << 5). */
+static double powf_log2(UINT32 ix)
+{
+    static const struct {
+        double invc, logc;
+    } T[] = {
+        { 0x1.661ec79f8f3bep+0, -0x1.efec65b963019p-2 * (1 << 5) },
+        { 0x1.571ed4aaf883dp+0, -0x1.b0b6832d4fca4p-2 * (1 << 5) },
+        { 0x1.49539f0f010bp+0, -0x1.7418b0a1fb77bp-2 * (1 << 5) },
+        { 0x1.3c995b0b80385p+0, -0x1.39de91a6dcf7bp-2 * (1 << 5) },
+        { 0x1.30d190c8864a5p+0, -0x1.01d9bf3f2b631p-2 * (1 << 5) },
+        { 0x1.25e227b0b8eap+0, -0x1.97c1d1b3b7afp-3 * (1 << 5) },
+        { 0x1.1bb4a4a1a343fp+0, -0x1.2f9e393af3c9fp-3 * (1 << 5) },
+        { 0x1.12358f08ae5bap+0, -0x1.960cbbf788d5cp-4 * (1 << 5) },
+        { 0x1.0953f419900a7p+0, -0x1.a6f9db6475fcep-5 * (1 << 5) },
+        { 0x1p+0, 0x0p+0 * (1 << 4) },
+        { 0x1.e608cfd9a47acp-1, 0x1.338ca9f24f53dp-4 * (1 << 5) },
+        { 0x1.ca4b31f026aap-1, 0x1.476a9543891bap-3 * (1 << 5) },
+        { 0x1.b2036576afce6p-1, 0x1.e840b4ac4e4d2p-3 * (1 << 5) },
+        { 0x1.9c2d163a1aa2dp-1, 0x1.40645f0c6651cp-2 * (1 << 5) },
+        { 0x1.886e6037841edp-1, 0x1.88e9c2c1b9ff8p-2 * (1 << 5) },
+        { 0x1.767dcf5534862p-1, 0x1.ce0a44eb17bccp-2 * (1 << 5) }
+    };
+    static const double A[] = {
+        0x1.27616c9496e0bp-2 * (1 << 5), -0x1.71969a075c67ap-2 * (1 << 5),
+        0x1.ec70a6ca7baddp-2 * (1 << 5), -0x1.7154748bef6c8p-1 * (1 << 5),
+        0x1.71547652ab82bp0 * (1 << 5)
+    };
+
+    double z, r, r2, r4, p, q, y, y0, invc, logc;
+    UINT32 iz, top, tmp;
+    int k, i;
+
+    /* x = 2^k z; where z is in range [OFF,2*OFF] and exact.
+       The range is split into N subintervals.
+       The ith subinterval contains z and c is near its center. */
+    tmp = ix - 0x3f330000;
+    i = (tmp >> (23 - 4)) % (1 << 4);
+    top = tmp & 0xff800000;
+    iz = ix - top;
+    k = (INT32)top >> (23 - 5); /* arithmetic shift */
+    invc = T[i].invc;
+    logc = T[i].logc;
+    z = *(float*)&iz;
+
+    /* log2(x) = log1p(z/c-1)/ln2 + log2(c) + k */
+    r = z * invc - 1;
+    y0 = logc + (double)k;
+
+    /* Pipelined polynomial evaluation to approximate log1p(r)/ln2. */
+    r2 = r * r;
+    y = A[0] * r + A[1];
+    p = A[2] * r + A[3];
+    r4 = r2 * r2;
+    q = A[4] * r + y0;
+    q = p * r2 + q;
+    y = y * r4 + q;
+    return y;
+}
+
+/* The output of log2 and thus the input of exp2 is either scaled by N
+   (in case of fast toint intrinsics) or not. The unscaled xd must be
+   in [-1021,1023], sign_bias sets the sign of the result. */
+static float powf_exp2(double xd, UINT32 sign_bias)
+{
+    static const double C[] = {
+        0x1.c6af84b912394p-5 / (1 << 5) / (1 << 5) / (1 << 5),
+        0x1.ebfce50fac4f3p-3 / (1 << 5) / (1 << 5),
+        0x1.62e42ff0c52d6p-1 / (1 << 5)
+    };
+
+    UINT64 ki, ski, t;
+    double kd, z, r, r2, y, s;
+
+    /* N*x = k + r with r in [-1/2, 1/2] */
+    kd = __round(xd); /* k */
+    ki = kd;
+    r = xd - kd;
+
+    /* exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) */
+    t = exp2f_T[ki % (1 << 5)];
+    ski = ki + sign_bias;
+    t += ski << (52 - 5);
+    s = *(double*)&t;
+    z = C[0] * r + C[1];
+    r2 = r * r;
+    y = C[2] * r + 1;
+    y = z * r2 + y;
+    y = y * s;
+    return y;
+}
+
+/* Returns 0 if not int, 1 if odd int, 2 if even int. The argument is
+   the bit representation of a non-zero finite floating-point value. */
+static int powf_checkint(UINT32 iy)
+{
+    int e = iy >> 23 & 0xff;
+    if (e < 0x7f)
+        return 0;
+    if (e > 0x7f + 23)
+        return 2;
+    if (iy & ((1 << (0x7f + 23 - e)) - 1))
+        return 0;
+    if (iy & (1 << (0x7f + 23 - e)))
+        return 1;
+    return 2;
+}
+
 /*********************************************************************
  *      powf (MSVCRT.@)
+ *
+ * Copied from musl: src/math/powf.c src/math/powf_data.c
  */
 float CDECL powf( float x, float y )
 {
-  float z = unix_funcs->powf(x,y);
-  if (x < 0 && y != floorf(y)) return math_error(_DOMAIN, "powf", x, y, z);
-  if (!x && isfinite(y) && y < 0) return math_error(_SING, "powf", x, y, z);
-  if (isfinite(x) && isfinite(y) && !isfinite(z)) return math_error(_OVERFLOW, "powf", x, y, z);
-  if (x && isfinite(x) && isfinite(y) && !z) return math_error(_UNDERFLOW, "powf", x, y, z);
-  return z;
+    UINT32 sign_bias = 0;
+    UINT32 ix, iy;
+    double logx, ylogx;
+
+    ix = *(UINT32*)&x;
+    iy = *(UINT32*)&y;
+    if (ix - 0x00800000 >= 0x7f800000 - 0x00800000 ||
+            2 * iy - 1 >= 2u * 0x7f800000 - 1) {
+        /* Either (x < 0x1p-126 or inf or nan) or (y is 0 or inf or nan). */
+        if (2 * iy - 1 >= 2u * 0x7f800000 - 1) {
+            if (2 * iy == 0)
+                return 1.0f;
+            if (ix == 0x3f800000)
+                return 1.0f;
+            if (2 * ix > 2u * 0x7f800000 || 2 * iy > 2u * 0x7f800000)
+                return x + y;
+            if (2 * ix == 2 * 0x3f800000)
+                return 1.0f;
+            if ((2 * ix < 2 * 0x3f800000) == !(iy & 0x80000000))
+                return 0.0f; /* |x|<1 && y==inf or |x|>1 && y==-inf. */
+            return y * y;
+        }
+        if (2 * ix - 1 >= 2u * 0x7f800000 - 1) {
+            float x2 = x * x;
+            if (ix & 0x80000000 && powf_checkint(iy) == 1)
+                x2 = -x2;
+            if (iy & 0x80000000 && x2 == 0.0)
+                return math_error(_SING, "powf", x, 0, 1 / x2);
+            /* Without the barrier some versions of clang hoist the 1/x2 and
+               thus division by zero exception can be signaled spuriously. */
+            return iy & 0x80000000 ? fp_barrierf(1 / x2) : x2;
+        }
+        /* x and y are non-zero finite. */
+        if (ix & 0x80000000) {
+            /* Finite x < 0. */
+            int yint = powf_checkint(iy);
+            if (yint == 0)
+                return math_error(_DOMAIN, "powf", x, 0, 0 / (x - x));
+            if (yint == 1)
+                sign_bias = 1 << (5 + 11);
+            ix &= 0x7fffffff;
+        }
+        if (ix < 0x00800000) {
+            /* Normalize subnormal x so exponent becomes negative. */
+            x *= 0x1p23f;
+            ix = *(UINT32*)&x;
+            ix &= 0x7fffffff;
+            ix -= 23 << 23;
+        }
+    }
+    logx = powf_log2(ix);
+    ylogx = y * logx; /* cannot overflow, y is single prec. */
+    if ((*(UINT64*)&ylogx >> 47 & 0xffff) >= 0x40af800000000000llu >> 47) {
+        /* |y*log(x)| >= 126. */
+        if (ylogx > 0x1.fffffffd1d571p+6 * (1 << 5))
+            return math_error(_OVERFLOW, "powf", x, 0, (sign_bias ? -1.0 : 1.0) * 0x1p1023);
+        if (ylogx <= -150.0 * (1 << 5))
+            return math_error(_UNDERFLOW, "powf", x, 0, (sign_bias ? -1.0 : 1.0) / 0x1p1023);
+    }
+    return powf_exp2(ylogx, sign_bias);
 }
 
 /*********************************************************************
diff --git a/dlls/msvcrt/unixlib.c b/dlls/msvcrt/unixlib.c
index 0498a736812..2b189246084 100644
--- a/dlls/msvcrt/unixlib.c
+++ b/dlls/msvcrt/unixlib.c
@@ -70,20 +70,11 @@ static double CDECL unix_pow( double x, double y )
     return pow( x, y );
 }
 
-/*********************************************************************
- *      powf
- */
-static float CDECL unix_powf( float x, float y )
-{
-    return powf( x, y );
-}
-
 static const struct unix_funcs funcs =
 {
     unix_exp,
     unix_exp2,
     unix_pow,
-    unix_powf,
 };
 
 NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out )
diff --git a/dlls/msvcrt/unixlib.h b/dlls/msvcrt/unixlib.h
index 21bb5e6c130..e82977e4097 100644
--- a/dlls/msvcrt/unixlib.h
+++ b/dlls/msvcrt/unixlib.h
@@ -26,7 +26,6 @@ struct unix_funcs
     double          (CDECL *exp)(double x);
     double          (CDECL *exp2)(double x);
     double          (CDECL *pow)(double x, double y);
-    float           (CDECL *powf)(float x, float y);
 };
 
 #endif /* __UNIXLIB_H */




More information about the wine-cvs mailing list