catanh: Use approximations for small real part

Signed-off-by: Martin Storsjö <martin@martin.st>
diff --git a/mingw-w64-crt/complex/catanh.def.h b/mingw-w64-crt/complex/catanh.def.h
index 68949d1..1d46d4d 100644
--- a/mingw-w64-crt/complex/catanh.def.h
+++ b/mingw-w64-crt/complex/catanh.def.h
@@ -75,17 +75,42 @@
   if (r_class == FP_ZERO && i_class == FP_ZERO)
     return z;
 
+  /* catanh(z) = 1/2 * clog(1+z) - 1/2 * clog(1-z) = 1/2 * clog((1+z)/(1-z)) */
+
+  /* Use identity clog(c) = 1/2*log(|c|^2) + i*arg(c) to calculate real and
+  imaginary parts separately. */
+
+  /* real part */
+  /* |c|^2 = (Im(z)^2 + (1+Re(z))^2)/(Im(z)^2 + (1-Re(z))^2) */
   i2 = __imag__ z * __imag__ z;
 
-  n = __FLT_CST(1.0) + __real__ z;
-  n = i2 + n * n;
+  if (__FLT_ABI(fabs) (__real__ z) <= __FLT_EPSILON)
+  {
+    /* |c|^2 = 1 + 4*Re(z)/(1+Im(z)^2) + O(Re(z)^2)  (Taylor series) */
+    __real__ ret = __FLT_CST(0.25) *
+      __FLT_ABI(log1p) (__FLT_CST(4.0)*(__real__ z) / (__FLT_CST(1.0) + i2));
+  }
+  else if ((__real__ z)*(__real__ z) <= __FLT_EPSILON)
+  {
+    /* |c|^2 = 1 + 4*Re(z)/(1+Im(z)^2) + 8*Re(z)^2/(1+Im(z)^2)^2 + O(Re(z)^3)  (Taylor series) */
+    d = __real__ z / (__FLT_CST(1.0) + i2);
+    __real__ ret = __FLT_CST(0.25) *
+      __FLT_ABI(log1p) (__FLT_CST(4.0) * d * (__FLT_CST(1.0) + __FLT_CST(2.0) * d));
+  }
+  else
+  {
+    n = __FLT_CST(1.0) + __real__ z;
+    n = i2 + n * n;
 
-  d = __FLT_CST(1.0) - __real__ z;
-  d = i2 + d * d;
+    d = __FLT_CST(1.0) - __real__ z;
+    d = i2 + d * d;
 
-  __real__ ret = __FLT_CST(0.25) * (__FLT_ABI(log) (n) - __FLT_ABI(log) (d));
+    __real__ ret = __FLT_CST(0.25) * (__FLT_ABI(log) (n) - __FLT_ABI(log) (d));
+  }
 
-  d = 1 - __real__ z * __real__ z - i2;
+  /* imaginary part */
+  /* z = (1 - Re(z)^2 - Im(z)^2 + 2i * Im(z) / ((1-Re(z))^2 + Im(z)^2) */
+  d = __FLT_CST(1.0) - __real__ z * __real__ z - i2;
 
   __imag__ ret = __FLT_CST(0.5) * __FLT_ABI(atan2) (__FLT_CST(2.0) * __imag__ z, d);