math: tan cleanups
authorSzabolcs Nagy <nsz@port70.net>
Sat, 18 May 2013 12:34:00 +0000 (12:34 +0000)
committerSzabolcs Nagy <nsz@port70.net>
Sat, 18 May 2013 12:34:00 +0000 (12:34 +0000)
* use unsigned arithmetics on the representation
* store arg reduction quotient in unsigned (so n%2 would work like n&1)
* use different convention to pass the arg reduction bit to __tan
  (this argument used to be 1 for even and -1 for odd reduction
  which meant obscure bithacks, the new n&1 is cleaner)
* raise inexact and underflow flags correctly for small x
  (tanl(x) may still raise spurious underflow for small but normal x)
  (this exception raising code increases codesize a bit, similar fixes
  are needed in many other places, it may worth investigating at some
  point if the inexact and underflow flags are worth raising correctly
  as this is not strictly required by the standard)
* tanf manual reduction optimization is kept for now
* tanl code path is cleaned up to follow similar logic to tan and tanf

src/math/__tan.c
src/math/__tandf.c
src/math/__tanl.c
src/math/tan.c
src/math/tanf.c
src/math/tanl.c

index fc739f95fd3a26c91799809633f0d2ec459305cb..8019844d3bc2dac0916290ecb5c05a63e8b7ffc8 100644 (file)
@@ -12,7 +12,7 @@
  * kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854
  * Input x is assumed to be bounded by ~pi/4 in magnitude.
  * Input y is the tail of x.
- * Input k indicates whether tan (if k = 1) or -1/tan (if k = -1) is returned.
+ * Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned.
  *
  * Algorithm
  *      1. Since tan(-x) = -tan(x), we need only to consider positive x.
@@ -63,21 +63,22 @@ static const double T[] = {
 pio4 =       7.85398163397448278999e-01, /* 3FE921FB, 54442D18 */
 pio4lo =     3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
 
-double __tan(double x, double y, int iy)
+double __tan(double x, double y, int odd)
 {
-       double_t z, r, v, w, s, sign;
-       int32_t ix, hx;
+       double_t z, r, v, w, s, a;
+       double w0, a0;
+       uint32_t hx;
+       int big, sign;
 
        GET_HIGH_WORD(hx,x);
-       ix = hx & 0x7fffffff;    /* high word of |x| */
-       if (ix >= 0x3FE59428) {  /* |x| >= 0.6744 */
-               if (hx < 0) {
+       big = (hx&0x7fffffff) >= 0x3FE59428; /* |x| >= 0.6744 */
+       if (big) {
+               sign = hx>>31;
+               if (sign) {
                        x = -x;
                        y = -y;
                }
-               z = pio4 - x;
-               w = pio4lo - y;
-               x = z + w;
+               x = (pio4 - x) + (pio4lo - y);
                y = 0.0;
        }
        z = x * x;
@@ -90,30 +91,20 @@ double __tan(double x, double y, int iy)
        r = T[1] + w*(T[3] + w*(T[5] + w*(T[7] + w*(T[9] + w*T[11]))));
        v = z*(T[2] + w*(T[4] + w*(T[6] + w*(T[8] + w*(T[10] + w*T[12])))));
        s = z * x;
-       r = y + z * (s * (r + v) + y);
-       r += T[0] * s;
+       r = y + z*(s*(r + v) + y) + s*T[0];
        w = x + r;
-       if (ix >= 0x3FE59428) {
-               v = iy;
-               sign = 1 - ((hx >> 30) & 2);
-               return sign * (v - 2.0 * (x - (w * w / (w + v) - r)));
+       if (big) {
+               s = 1 - 2*odd;
+               v = s - 2.0 * (x + (r - w*w/(w + s)));
+               return sign ? -v : v;
        }
-       if (iy == 1)
+       if (!odd)
                return w;
-       else {
-               /*
-                * if allow error up to 2 ulp, simply return
-                * -1.0 / (x+r) here
-                */
-               /* compute -1.0 / (x+r) accurately */
-               double_t a;
-               double z, t;
-               z = w;
-               SET_LOW_WORD(z,0);
-               v = r - (z - x);        /* z+v = r+x */
-               t = a = -1.0 / w;       /* a = -1.0/w */
-               SET_LOW_WORD(t,0);
-               s = 1.0 + t * z;
-               return t + a * (s + t * v);
-       }
+       /* -1.0/(x+r) has up to 2ulp error, so compute it accurately */
+       w0 = w;
+       SET_LOW_WORD(w0, 0);
+       v = r - (w0 - x);       /* w0+v = r+x */
+       a0 = a = -1.0 / w;
+       SET_LOW_WORD(a0, 0);
+       return a0 + a*(1.0 + a0*w0 + a0*v);
 }
index 3e632fdf4abce4558b7850b288d9f45c52833449..25047eeee9c098894010a3984372ba63cb698656 100644 (file)
@@ -25,7 +25,7 @@ static const double T[] = {
   0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */
 };
 
-float __tandf(double x, int iy)
+float __tandf(double x, int odd)
 {
        double_t z,r,w,s,t,u;
 
@@ -50,6 +50,5 @@ float __tandf(double x, int iy)
        s = z*x;
        u = T[0] + z*T[1];
        r = (x + s*u) + (s*w)*(t + w*r);
-       if(iy==1) return r;
-       else return -1.0/r;
+       return odd ? -1.0/r : r;
 }
index 50ba21409444d2735a612e3d4d065fbf535bb96a..4b36e616db9f344edeea9a3fc6f3659cd63f5bea 100644 (file)
@@ -45,25 +45,21 @@ T29 =  0.0000078293456938132840,        /*  0x106b59141a6cb3.0p-69 */
 T31 = -0.0000032609076735050182,        /* -0x1b5abef3ba4b59.0p-71 */
 T33 =  0.0000023261313142559411;        /*  0x13835436c0c87f.0p-71 */
 
-long double __tanl(long double x, long double y, int iy) {
+long double __tanl(long double x, long double y, int odd) {
        long double z, r, v, w, s, a, t;
-       long double osign;
-       int i;
+       int big, sign;
 
-       iy = iy == 1 ? -1 : 1;        /* XXX recover original interface */
-       osign = copysignl(1.0, x);
-       if (fabsl(x) >= 0.67434) {
+       big = fabsl(x) >= 0.67434;
+       if (big) {
+               sign = 0;
                if (x < 0) {
+                       sign = 1;
                        x = -x;
                        y = -y;
                }
-               z = pio4 - x;
-               w = pio4lo - y;
-               x = z + w;
+               x = (pio4 - x) + (pio4lo - y);
                y = 0.0;
-               i = 1;
-       } else
-               i = 0;
+       }
        z = x * x;
        w = z * z;
        r = T5 + w * (T9 + w * (T13 + w * (T17 + w * (T21 +
@@ -71,14 +67,14 @@ long double __tanl(long double x, long double y, int iy) {
        v = z * (T7 + w * (T11 + w * (T15 + w * (T19 + w * (T23 +
             w * (T27 + w * T31))))));
        s = z * x;
-       r = y + z * (s * (r + v) + y);
-       r += T3 * s;
+       r = y + z * (s * (r + v) + y) + T3 * s;
        w = x + r;
-       if (i == 1) {
-               v = (long double)iy;
-               return osign * (v - 2.0 * (x - (w * w / (w + v) - r)));
+       if (big) {
+               s = 1 - 2*odd;
+               v = s - 2.0 * (x + (r - w * w / (w + s)));
+               return sign ? -v : v;
        }
-       if (iy == 1)
+       if (!odd)
                return w;
 
        /*
index 2e1f3c832b3253e8ada06413480d46478614fd3d..9c724a45af51e09f9da9069ba7ab1073e8fee05c 100644 (file)
 
 double tan(double x)
 {
-       double y[2], z = 0.0;
-       int32_t n, ix;
+       double y[2];
+       uint32_t ix;
+       unsigned n;
 
-       /* High word of x. */
        GET_HIGH_WORD(ix, x);
+       ix &= 0x7fffffff;
 
        /* |x| ~< pi/4 */
-       ix &= 0x7fffffff;
        if (ix <= 0x3fe921fb) {
-               if (ix < 0x3e400000) /* x < 2**-27 */
-                       /* raise inexact if x != 0 */
-                       if ((int)x == 0)
-                               return x;
-               return __tan(x, z, 1);
+               if (ix < 0x3e400000) { /* |x| < 2**-27 */
+                       /* raise inexact if x!=0 and underflow if subnormal */
+                       FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+                       return x;
+               }
+               return __tan(x, 0.0, 0);
        }
 
        /* tan(Inf or NaN) is NaN */
        if (ix >= 0x7ff00000)
                return x - x;
 
-       /* argument reduction needed */
+       /* argument reduction */
        n = __rem_pio2(x, y);
-       return __tan(y[0], y[1], 1 - ((n&1)<<1)); /* n even: 1, n odd: -1 */
+       return __tan(y[0], y[1], n&1);
 }
index 8b0dfb20a14956fd6573371c6a683a8073e9db75..aba197777d3aeb0dfb5621f92ade9f0e7615223c 100644 (file)
@@ -26,37 +26,39 @@ t4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
 float tanf(float x)
 {
        double y;
-       int32_t n, hx, ix;
+       uint32_t ix;
+       unsigned n, sign;
 
-       GET_FLOAT_WORD(hx, x);
-       ix = hx & 0x7fffffff;
+       GET_FLOAT_WORD(ix, x);
+       sign = ix >> 31;
+       ix &= 0x7fffffff;
 
        if (ix <= 0x3f490fda) {  /* |x| ~<= pi/4 */
-               if (ix < 0x39800000)  /* |x| < 2**-12 */
-                       /* return x and raise inexact if x != 0 */
-                       if ((int)x == 0)
-                               return x;
-               return __tandf(x, 1);
+               if (ix < 0x39800000) {  /* |x| < 2**-12 */
+                       /* raise inexact if x!=0 and underflow if subnormal */
+                       FORCE_EVAL(ix < 0x00800000 ? x/0x1p120f : x+0x1p120f);
+                       return x;
+               }
+               return __tandf(x, 0);
        }
        if (ix <= 0x407b53d1) {  /* |x| ~<= 5*pi/4 */
                if (ix <= 0x4016cbe3)  /* |x| ~<= 3pi/4 */
-                       return __tandf((hx > 0 ? x-t1pio2 : x+t1pio2), -1);
+                       return __tandf((sign ? x+t1pio2 : x-t1pio2), 1);
                else
-                       return __tandf((hx > 0 ? x-t2pio2 : x+t2pio2), 1);
+                       return __tandf((sign ? x+t2pio2 : x-t2pio2), 0);
        }
        if (ix <= 0x40e231d5) {  /* |x| ~<= 9*pi/4 */
                if (ix <= 0x40afeddf)  /* |x| ~<= 7*pi/4 */
-                       return __tandf((hx > 0 ? x-t3pio2 : x+t3pio2), -1);
+                       return __tandf((sign ? x+t3pio2 : x-t3pio2), 1);
                else
-                       return __tandf((hx > 0 ? x-t4pio2 : x+t4pio2), 1);
+                       return __tandf((sign ? x+t4pio2 : x-t4pio2), 0);
        }
 
        /* tan(Inf or NaN) is NaN */
        if (ix >= 0x7f800000)
                return x - x;
 
-       /* general argument reduction needed */
+       /* argument reduction */
        n = __rem_pio2f(x, &y);
-       /* integer parameter: n even: 1; n odd: -1 */
-       return __tandf(y, 1-((n&1)<<1));
+       return __tandf(y, n&1);
 }
index 0194eaf7a9d88c6a4949fdc571ef4c40be9d83ac..3b51e011b0fc5cc1a76a88d2a2c9a4da3d171fa4 100644 (file)
@@ -41,42 +41,27 @@ long double tanl(long double x)
 long double tanl(long double x)
 {
        union IEEEl2bits z;
-       int e0, s;
        long double y[2];
-       long double hi, lo;
+       unsigned n;
 
        z.e = x;
-       s = z.bits.sign;
        z.bits.sign = 0;
 
-       /* If x = +-0 or x is subnormal, then tan(x) = x. */
-       if (z.bits.exp == 0)
-               return x;
-
        /* If x = NaN or Inf, then tan(x) = NaN. */
-       if (z.bits.exp == 32767)
+       if (z.bits.exp == 0x7fff)
                return (x - x) / (x - x);
 
-       /* Optimize the case where x is already within range. */
+       /* |x| < (double)pi/4 */
        if (z.e < M_PI_4) {
-               hi = __tanl(z.e, 0, 0);
-               return s ? -hi : hi;
+               /* x = +-0 or x is subnormal */
+               if (z.bits.exp == 0)
+                       /* inexact and underflow if x!=0 */
+                       return x + x*0x1p-120f;
+               /* can raise spurious underflow */
+               return __tanl(x, 0, 0);
        }
 
-       e0 = __rem_pio2l(x, y);
-       hi = y[0];
-       lo = y[1];
-
-       switch (e0 & 3) {
-       case 0:
-       case 2:
-               hi = __tanl(hi, lo, 0);
-               break;
-       case 1:
-       case 3:
-               hi = __tanl(hi, lo, 1);
-               break;
-       }
-       return hi;
+       n = __rem_pio2l(x, y);
+       return __tanl(y[0], y[1], n&1);
 }
 #endif