about summary refs log tree commit diff
path: root/math/s_ctanf.c
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2012-04-09 22:31:35 +0000
committerJoseph Myers <joseph@codesourcery.com>2012-04-09 22:31:35 +0000
commitbcc8d6617ba029c288fff9680a02b9a3b1caa9c0 (patch)
tree9cb163731d4d165bfe71bad55f59d88c6b420917 /math/s_ctanf.c
parent8a1fbaaf75536088b4d505409e0e87975d8cf8d5 (diff)
downloadglibc-bcc8d6617ba029c288fff9680a02b9a3b1caa9c0.tar.gz
glibc-bcc8d6617ba029c288fff9680a02b9a3b1caa9c0.tar.xz
glibc-bcc8d6617ba029c288fff9680a02b9a3b1caa9c0.zip
Fix ctan, ctanh overflow (bug 11521).
Diffstat (limited to 'math/s_ctanf.c')
-rw-r--r--math/s_ctanf.c44
1 files changed, 32 insertions, 12 deletions
diff --git a/math/s_ctanf.c b/math/s_ctanf.c
index 5f7f28ad07..4cba559a44 100644
--- a/math/s_ctanf.c
+++ b/math/s_ctanf.c
@@ -1,5 +1,5 @@
 /* Complex tangent function for float.
-   Copyright (C) 1997, 2005, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-2012 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
 
@@ -21,7 +21,7 @@
 #include <fenv.h>
 #include <math.h>
 #include <math_private.h>
-
+#include <float.h>
 
 __complex__ float
 __ctanf (__complex__ float x)
@@ -50,25 +50,45 @@ __ctanf (__complex__ float x)
     }
   else
     {
-      float sin2rx, cos2rx;
+      float sinrx, cosrx;
       float den;
+      const int t = (int) ((FLT_MAX_EXP - 1) * M_LN2 / 2);
 
-      __sincosf (2.0 * __real__ x, &sin2rx, &cos2rx);
-
-      den = cos2rx + __ieee754_coshf (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). */
 
+      __sincosf (__real__ x, &sinrx, &cosrx);
 
-      if (den == 0.0)
+      if (fabsf (__imag__ x) > t)
 	{
-	  __complex__ float ez = __cexpf (1.0i * x);
-	  __complex__ float emz = __cexpf (-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).  */
+	  float exp_2t = __ieee754_expf (2 * t);
 
-	  res = (ez - emz) / (ez + emz) * -1.0i;
+	  __imag__ res = __copysignf (1.0, __imag__ x);
+	  __real__ res = 4 * sinrx * cosrx;
+	  __imag__ x = fabsf (__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_expf (2 * __imag__ x);
 	}
       else
 	{
-	  __real__ res = sin2rx / den;
-	  __imag__ res = __ieee754_sinhf (2.0 * __imag__ x) / den;
+	  float sinhix = __ieee754_sinhf (__imag__ x);
+	  float coshix = __ieee754_coshf (__imag__ x);
+
+	  den = cosrx * cosrx + sinhix * sinhix;
+	  __real__ res = sinrx * cosrx / den;
+	  __imag__ res = sinhix * coshix / den;
 	}
     }