Compute correct sign for ctanh (Inf + iy) (BZ #17118)

Message ID mvmbnrr6n56.fsf@hawking.suse.de
State Superseded
Headers

Commit Message

Andreas Schwab Aug. 11, 2014, 3:50 p.m. UTC
  [BZ #17118]
	* math/s_ctanh.c (__ctanh): Compute correct sign for Inf + iy.
	* math/s_ctanhf.c (__ctanhf): Likewise.
	* math/s_ctanhl.c (__ctanhl): Likewise.
	* math/libm-test.inc (ctanh_test_data): Add tests.
---
 math/libm-test.inc | 12 ++++++++++++
 math/s_ctanh.c     |  9 ++++++++-
 math/s_ctanhf.c    |  9 ++++++++-
 math/s_ctanhl.c    |  9 ++++++++-
 4 files changed, 36 insertions(+), 3 deletions(-)
  

Comments

Joseph Myers Aug. 11, 2014, 3:59 p.m. UTC | #1
On Mon, 11 Aug 2014, Andreas Schwab wrote:

> @@ -32,8 +32,15 @@ __ctanh (__complex__ double x)
>      {
>        if (__isinf_ns (__real__ x))
>  	{
> +	  int icls = fpclassify (__imag__ x);
> +	  double sin2ix;
> +
> +	  if (__glibc_likely (icls != FP_SUBNORMAL))
> +	    sin2ix = sin (2 * __imag__ x);

That looks like it will cause spurious overflows if 2 * __imag__ x 
overflows (you can use sin * cos to get the right sign instead).
  
Joseph Myers Aug. 11, 2014, 4:40 p.m. UTC | #2
On Mon, 11 Aug 2014, Joseph S. Myers wrote:

> On Mon, 11 Aug 2014, Andreas Schwab wrote:
> 
> > @@ -32,8 +32,15 @@ __ctanh (__complex__ double x)
> >      {
> >        if (__isinf_ns (__real__ x))
> >  	{
> > +	  int icls = fpclassify (__imag__ x);
> > +	  double sin2ix;
> > +
> > +	  if (__glibc_likely (icls != FP_SUBNORMAL))
> > +	    sin2ix = sin (2 * __imag__ x);
> 
> That looks like it will cause spurious overflows if 2 * __imag__ x 
> overflows (you can use sin * cos to get the right sign instead).

(sin * cos can be computed using __sincos, of course.  To avoid possible 
underflow exceptions from sin (DBL_MIN), fabs (__imag__ x) > something 
may be a better condition than checking FP_SUBNORMAL.)

Also:

* What about arguments +/- Inf +/- iInf?  sin (Inf) will raise INVALID, 
but Annex G doesn't mention that as valid for this case of ctanh.

(Both these points indicate missing testsuite coverage.)

* ctan looks like it has the same issues as ctanh, so both should be 
fixed, and testsuite coverage added, together.
  

Patch

diff --git a/math/libm-test.inc b/math/libm-test.inc
index f86a4fa..0da6f31 100644
--- a/math/libm-test.inc
+++ b/math/libm-test.inc
@@ -6669,12 +6669,24 @@  static const struct test_c_c_data ctanh_test_data[] =
   {
     TEST_c_c (ctanh, plus_infty, 0, 1.0, 0.0),
     TEST_c_c (ctanh, plus_infty, 1, 1.0, 0.0),
+    TEST_c_c (ctanh, plus_infty, 2, 1.0, minus_zero),
+    TEST_c_c (ctanh, plus_infty, 3, 1.0, minus_zero),
+    TEST_c_c (ctanh, plus_infty, 4, 1.0, 0.0),
     TEST_c_c (ctanh, plus_infty, minus_zero, 1.0, minus_zero),
     TEST_c_c (ctanh, plus_infty, -1, 1.0, minus_zero),
+    TEST_c_c (ctanh, plus_infty, -2, 1.0, 0.0),
+    TEST_c_c (ctanh, plus_infty, -3, 1.0, 0.0),
+    TEST_c_c (ctanh, plus_infty, -4, 1.0, minus_zero),
     TEST_c_c (ctanh, minus_infty, 0, -1.0, 0.0),
     TEST_c_c (ctanh, minus_infty, 1, -1.0, 0.0),
+    TEST_c_c (ctanh, minus_infty, 2, -1.0, minus_zero),
+    TEST_c_c (ctanh, minus_infty, 3, -1.0, minus_zero),
+    TEST_c_c (ctanh, minus_infty, 4, -1.0, 0.0),
     TEST_c_c (ctanh, minus_infty, minus_zero, -1.0, minus_zero),
     TEST_c_c (ctanh, minus_infty, -1, -1.0, minus_zero),
+    TEST_c_c (ctanh, minus_infty, -2, -1.0, 0.0),
+    TEST_c_c (ctanh, minus_infty, -3, -1.0, 0.0),
+    TEST_c_c (ctanh, minus_infty, -4, -1.0, minus_zero),
 
     TEST_c_c (ctanh, 0, plus_infty, qnan_value, qnan_value, INVALID_EXCEPTION),
     TEST_c_c (ctanh, 2, plus_infty, qnan_value, qnan_value, INVALID_EXCEPTION),
diff --git a/math/s_ctanh.c b/math/s_ctanh.c
index be3e47e..98289d2 100644
--- a/math/s_ctanh.c
+++ b/math/s_ctanh.c
@@ -32,8 +32,15 @@  __ctanh (__complex__ double x)
     {
       if (__isinf_ns (__real__ x))
 	{
+	  int icls = fpclassify (__imag__ x);
+	  double sin2ix;
+
+	  if (__glibc_likely (icls != FP_SUBNORMAL))
+	    sin2ix = sin (2 * __imag__ x);
+	  else
+	    sin2ix = __imag__ x;
 	  __real__ res = __copysign (1.0, __real__ x);
-	  __imag__ res = __copysign (0.0, __imag__ x);
+	  __imag__ res = __copysign (0.0, sin2ix);
 	}
       else if (__imag__ x == 0.0)
 	{
diff --git a/math/s_ctanhf.c b/math/s_ctanhf.c
index 39a335e..1f6bab2 100644
--- a/math/s_ctanhf.c
+++ b/math/s_ctanhf.c
@@ -32,8 +32,15 @@  __ctanhf (__complex__ float x)
     {
       if (__isinf_nsf (__real__ x))
 	{
+	  int icls = fpclassify (__imag__ x);
+	  float sin2ix;
+
+	  if (__glibc_likely (icls != FP_SUBNORMAL))
+	    sin2ix = sinf (2 * __imag__ x);
+	  else
+	    sin2ix = __imag__ x;
 	  __real__ res = __copysignf (1.0, __real__ x);
-	  __imag__ res = __copysignf (0.0, __imag__ x);
+	  __imag__ res = __copysignf (0.0, sin2ix);
 	}
       else if (__imag__ x == 0.0)
 	{
diff --git a/math/s_ctanhl.c b/math/s_ctanhl.c
index 64e448b..5e4d838 100644
--- a/math/s_ctanhl.c
+++ b/math/s_ctanhl.c
@@ -32,8 +32,15 @@  __ctanhl (__complex__ long double x)
     {
       if (__isinf_nsl (__real__ x))
 	{
+	  int icls = fpclassify (__imag__ x);
+	  long double sin2ix;
+
+	  if (__glibc_likely (icls != FP_SUBNORMAL))
+	    sin2ix = sinl (2 * __imag__ x);
+	  else
+	    sin2ix = __imag__ x;
 	  __real__ res = __copysignl (1.0, __real__ x);
-	  __imag__ res = __copysignl (0.0, __imag__ x);
+	  __imag__ res = __copysignl (0.0, sin2ix);
 	}
       else if (__imag__ x == 0.0)
 	{