changeset 21945:e9765b62d4e8

Use C++11 standard complex trig functions when available (bug #44310, bug #45507) * acinclude.m4 (OCTAVE_CHECK_FUNC_COMPLEX): New macro. * configure.ac: Use it to test for complex variants of trig and hyperbolic functions. * lo-mappers.cc (octave::math::acos, octave::math::asin, octave::math::atan): Use standard library functions when available. * lo-specfun.cc (octave::math::acosh, octave::math::asinh, octave::math::atanh): Likewise. * mappers.cc: Add BIST tests for affected functions.
author Mike Miller <mtmiller@octave.org>
date Fri, 17 Jun 2016 14:24:27 -0700
parents 4b2eab5d2a6a
children 835d070ede9c
files configure.ac libinterp/corefcn/mappers.cc liboctave/numeric/lo-mappers.cc liboctave/numeric/lo-specfun.cc m4/acinclude.m4
diffstat 5 files changed, 138 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Fri Jun 17 16:51:09 2016 -0400
+++ b/configure.ac	Fri Jun 17 14:24:27 2016 -0700
@@ -2578,6 +2578,15 @@
   ;;
 esac
 
+## Look in <complex> for C++ variants of math functions that we need.
+
+OCTAVE_CHECK_FUNC_COMPLEX(acos)
+OCTAVE_CHECK_FUNC_COMPLEX(acosh)
+OCTAVE_CHECK_FUNC_COMPLEX(asin)
+OCTAVE_CHECK_FUNC_COMPLEX(asinh)
+OCTAVE_CHECK_FUNC_COMPLEX(atan)
+OCTAVE_CHECK_FUNC_COMPLEX(atanh)
+
 ## Check for nonstandard, but common math functions, that we need.
 
 dnl Use multiple AC_CHECKs to avoid line continuations '\' in list
--- a/libinterp/corefcn/mappers.cc	Fri Jun 17 16:51:09 2016 -0400
+++ b/libinterp/corefcn/mappers.cc	Fri Jun 17 14:24:27 2016 -0700
@@ -125,6 +125,11 @@
 %! assert (acos ([2 0]),  [ival*i, pi/2], 2*eps);
 %! assert (acos ([2 0i]), [ival*i, pi/2], 2*eps);
 
+## Test large magnitude arguments (bug #45507)
+%! x = [1, -1, i, -i] .* 1e150;
+%! v = [0, pi, pi/2, pi/2];
+%! assert (real (acos (x)), v);
+
 %!error acos ()
 %!error acos (1, 2)
 */
@@ -164,6 +169,12 @@
 %! assert (acosh (single (10i)), re + i*im, 5*eps ("single"));
 %! assert (acosh (single (-10i)), re - i*im, 5*eps ("single"));
 
+## Test large magnitude arguments (bug #45507)
+%!test
+%! x = [1, -1, i, -i] .* 1e150;
+%! v = [0, pi, pi/2, -pi/2];
+%! assert (imag (acosh (x)), v);
+
 %!error acosh ()
 %!error acosh (1, 2)
 */
@@ -277,6 +288,11 @@
 %! assert (asin ([2 0]),  [rval - ival*i, 0], 2*eps);
 %! assert (asin ([2 0i]), [rval - ival*i, 0], 2*eps);
 
+## Test large magnitude arguments (bug #45507)
+%! x = [1, -1, i, -i] .* 1e150;
+%! v = [pi/2, -pi/2, 0, -0];
+%! assert (real (asin (x)), v);
+
 %!error asin ()
 %!error asin (1, 2)
 */
@@ -305,6 +321,11 @@
 %! x = single ([0, i, 0, -i]);
 %! assert (asinh (x), v,  sqrt (eps ("single")));
 
+## Test large magnitude arguments (bug #45507)
+%! x = [1, -1, i, -i] .* 1e150;
+%! v = [0, 0, pi/2, -pi/2];
+%! assert (imag (asinh (x)), v);
+
 %!error asinh ()
 %!error asinh (1, 2)
 */
@@ -337,6 +358,12 @@
 %! x = single ([0, rt3/3, 1, rt3, -rt3, -1, -rt3/3, 0]);
 %! assert (atan (x), v, sqrt (eps ("single")));
 
+## Test large magnitude arguments (bug #44310, bug #45507)
+%! x = [1, -1, i, -i] .* 1e150;
+%! v = [pi/2, -pi/2, pi/2, -pi/2];
+%! assert (real (atan (x)), v);
+%! assert (imag (atan (x)), [0, 0, 0, 0], eps);
+
 %!error atan ()
 %!error atan (1, 2)
 */
@@ -365,6 +392,12 @@
 %! x = single ([0, 0]);
 %! assert (atanh (x), v, sqrt (eps ("single")));
 
+## Test large magnitude arguments (bug #44310, bug #45507)
+%! x = [1, -1, i, -i] .* 1e150;
+%! v = [pi/2, pi/2, pi/2, -pi/2];
+%! assert (imag (atanh (x)), v);
+%! assert (real (atanh (x)), [0, 0, 0, 0], eps);
+
 %!error atanh ()
 %!error atanh (1, 2)
 */
--- a/liboctave/numeric/lo-mappers.cc	Fri Jun 17 16:51:09 2016 -0400
+++ b/liboctave/numeric/lo-mappers.cc	Fri Jun 17 14:24:27 2016 -0700
@@ -88,6 +88,14 @@
     Complex
     acos (const Complex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ACOS)
+      Complex y = std::acos (x);
+
+      if (imag (x) == 0.0 && real (x) > 1.0)
+        return conj (y);
+      else
+        return y;
+#else
       static Complex i (0, 1);
 
       Complex tmp;
@@ -104,11 +112,20 @@
         tmp = 1.0 - x*x;
 
       return -i * log (x + i * sqrt (tmp));
+#endif
     }
 
     FloatComplex
     acos (const FloatComplex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ACOS)
+      FloatComplex y = std::acos (x);
+
+      if (imag (x) == 0.0f && real (x) > 1.0f)
+        return conj (y);
+      else
+        return y;
+#else
       static FloatComplex i (0, 1);
 
       FloatComplex tmp;
@@ -125,11 +142,20 @@
         tmp = 1.0f - x*x;
 
       return -i * log (x + i * sqrt (tmp));
+#endif
     }
 
     Complex
     asin (const Complex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ASIN)
+      Complex y = std::asin (x);
+
+      if (imag (x) == 0.0 && real (x) > 1.0)
+        return conj (y);
+      else
+        return y;
+#else
       static Complex i (0, 1);
 
       Complex tmp;
@@ -146,11 +172,20 @@
         tmp = 1.0 - x*x;
 
       return -i * log (i*x + sqrt (tmp));
+#endif
     }
 
     FloatComplex
     asin (const FloatComplex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ASIN)
+      FloatComplex y = std::asin (x);
+
+      if (imag (x) == 0.0f && real (x) > 1.0f)
+        return conj (y);
+      else
+        return y;
+#else
       static FloatComplex i (0, 1);
 
       FloatComplex tmp;
@@ -167,22 +202,31 @@
         tmp = 1.0f - x*x;
 
       return -i * log (i*x + sqrt (tmp));
+#endif
     }
 
     Complex
     atan (const Complex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ATAN)
+      return std::atan (x);
+#else
       static Complex i (0, 1);
 
       return i * log ((i + x) / (i - x)) / 2.0;
+#endif
     }
 
     FloatComplex
     atan (const FloatComplex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ATAN)
+      return std::atan (x);
+#else
       static FloatComplex i (0, 1);
 
       return i * log ((i + x) / (i - x)) / 2.0f;
+#endif
     }
 
     double log2 (double x) { return std::log2 (x); }
--- a/liboctave/numeric/lo-specfun.cc	Fri Jun 17 16:51:09 2016 -0400
+++ b/liboctave/numeric/lo-specfun.cc	Fri Jun 17 14:24:27 2016 -0700
@@ -229,13 +229,21 @@
     Complex
     acosh (const Complex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ACOSH)
+      return std::acosh (x);
+#else
       return log (x + sqrt (x + 1.0) * sqrt (x - 1.0));
+#endif
     }
 
     FloatComplex
     acosh (const FloatComplex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ACOSH)
+      return std::acosh (x);
+#else
       return log (x + sqrt (x + 1.0f) * sqrt (x - 1.0f));
+#endif
     }
 
     double
@@ -265,13 +273,21 @@
     Complex
     asinh (const Complex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ASINH)
+      return std::asinh (x);
+#else
       return log (x + sqrt (x*x + 1.0));
+#endif
     }
 
     FloatComplex
     asinh (const FloatComplex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ASINH)
+      return std::asinh (x);
+#else
       return log (x + sqrt (x*x + 1.0f));
+#endif
     }
 
     double
@@ -301,13 +317,21 @@
     Complex
     atanh (const Complex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ATANH)
+      return std::atanh (x);
+#else
       return log ((1.0 + x) / (1.0 - x)) / 2.0;
+#endif
     }
 
     FloatComplex
     atanh (const FloatComplex& x)
     {
+#if defined (HAVE_COMPLEX_STD_ATANH)
+      return std::atanh (x);
+#else
       return log ((1.0f + x) / (1.0f - x)) / 2.0f;
+#endif
     }
 
     double
--- a/m4/acinclude.m4	Fri Jun 17 16:51:09 2016 -0400
+++ b/m4/acinclude.m4	Fri Jun 17 14:24:27 2016 -0700
@@ -320,6 +320,34 @@
   fi
 ])
 dnl
+dnl Check whether a complex-valued function is available in <complex>.
+dnl Will define HAVE_COMPLEX_STD_FUNC if the function is available in the
+dnl std namespace and is callable on both std::complex<double> and
+dnl std::complex<float>.  The return type of the function is expected to
+dnl be of the same std::complex<T> type.
+dnl
+AC_DEFUN([OCTAVE_CHECK_FUNC_COMPLEX], [
+  ac_safe=`echo "$1" | $SED 'y% ./+-:=%___p___%'`
+
+  AC_CACHE_CHECK([for std::$1 in <complex>],
+    [octave_cv_func_complex_std_$ac_safe],
+    [AC_LANG_PUSH(C++)
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+        #include <complex>
+        ]], [[
+        std::complex<double> z = std::$1 (std::complex<double> (1.0, 1.0));
+        std::complex<float>  c = std::$1 (std::complex<float>  (1.0, 1.0));
+        ]])],
+      [eval "octave_cv_func_complex_std_$ac_safe=yes"],
+      [eval "octave_cv_func_complex_std_$ac_safe=no"])
+    AC_LANG_POP(C++)
+  ])
+  if eval "test \"`echo '$octave_cv_func_complex_std_'$ac_safe`\" = yes"; then
+    AC_DEFINE(AS_TR_CPP([[HAVE_COMPLEX_STD_][$1]]), 1,
+      [Define to 1 if <complex> provides std::$1(std::complex<T>).])
+  fi
+])
+dnl
 dnl Check whether Qscintilla has version 2.6.0 or later
 dnl FIXME: This test uses a version number.  It potentially could
 dnl        be re-written to actually call the function, but is it worth it?