]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - math/s_ctanl.c
Use glibc_likely instead __builtin_expect.
[thirdparty/glibc.git] / math / s_ctanl.c
index 112dd723d8bc6241820bf34d2ccaf871766b22d3..282febdd89e1f5b3419d53a82555671eed71fa99 100644 (file)
@@ -1,5 +1,5 @@
 /* Complex tangent function for long double.
-   Copyright (C) 1997, 2005, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-2014 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
 
 #include <complex.h>
 #include <fenv.h>
 #include <math.h>
-
 #include <math_private.h>
-
+#include <float.h>
 
 __complex__ long double
 __ctanl (__complex__ long double x)
 {
   __complex__ long double res;
 
-  if (__builtin_expect (!isfinite (__real__ x) || !isfinite (__imag__ x), 0))
+  if (__glibc_unlikely (!isfinite (__real__ x) || !isfinite (__imag__ x)))
     {
       if (__isinf_nsl (__imag__ x))
        {
@@ -51,25 +50,66 @@ __ctanl (__complex__ long double x)
     }
   else
     {
-      long double sin2rx, cos2rx;
+      long double sinrx, cosrx;
       long double den;
+      const int t = (int) ((LDBL_MAX_EXP - 1) * M_LN2l / 2);
+      int rcls = fpclassify (__real__ x);
 
-      __sincosl (2.0 * __real__ x, &sin2rx, &cos2rx);
-
-      den = cos2rx + __ieee754_coshl (2.0 * __imag__ x);
+      /* tan(x+iy) = (sin(2x) + i*sinh(2y))/(cos(2x) + cosh(2y))
+        = (sin(x)*cos(x) + i*sinh(y)*cosh(y)/(cos(x)^2 + sinh(y)^2). */
 
+      if (__glibc_likely (rcls != FP_SUBNORMAL))
+       {
+         __sincosl (__real__ x, &sinrx, &cosrx);
+       }
+      else
+       {
+         sinrx = __real__ x;
+         cosrx = 1.0;
+       }
 
-      if (den == 0.0)
+      if (fabsl (__imag__ x) > t)
        {
-         __complex__ long double ez = __cexpl (1.0i * x);
-         __complex__ long double emz = __cexpl (-1.0i * x);
+         /* Avoid intermediate overflow when the real part of the
+            result may be subnormal.  Ignoring negligible terms, the
+            imaginary part is +/- 1, the real part is
+            sin(x)*cos(x)/sinh(y)^2 = 4*sin(x)*cos(x)/exp(2y).  */
+         long double exp_2t = __ieee754_expl (2 * t);
 
-         res = (ez - emz) / (ez + emz) * -1.0i;
+         __imag__ res = __copysignl (1.0, __imag__ x);
+         __real__ res = 4 * sinrx * cosrx;
+         __imag__ x = fabsl (__imag__ x);
+         __imag__ x -= t;
+         __real__ res /= exp_2t;
+         if (__imag__ x > t)
+           {
+             /* Underflow (original imaginary part of x has absolute
+                value > 2t).  */
+             __real__ res /= exp_2t;
+           }
+         else
+           __real__ res /= __ieee754_expl (2 * __imag__ x);
        }
       else
        {
-         __real__ res = sin2rx / den;
-         __imag__ res = __ieee754_sinhl (2.0 * __imag__ x) / den;
+         long double sinhix, coshix;
+         if (fabsl (__imag__ x) > LDBL_MIN)
+           {
+             sinhix = __ieee754_sinhl (__imag__ x);
+             coshix = __ieee754_coshl (__imag__ x);
+           }
+         else
+           {
+             sinhix = __imag__ x;
+             coshix = 1.0L;
+           }
+
+         if (fabsl (sinhix) > fabsl (cosrx) * LDBL_EPSILON)
+           den = cosrx * cosrx + sinhix * sinhix;
+         else
+           den = cosrx * cosrx;
+         __real__ res = sinrx * cosrx / den;
+         __imag__ res = sinhix * coshix / den;
        }
     }