On SVID-based systems, when certain math functions encounter an
exceptional condition they call a magic function named matherr.  That
function is permitted to change the result which the math functions will
return.  a program is permitted to provide its own version of matherr.

Solaris works this way and at least the Solaris version of matherr does
not act as Go, or an ordinary interpretation of the function, would
expect.  E.g., calling acos on an out-of-range value will return 0
rather than NaN.

This patch defines a version of matherr in the Go library to ensure that
the Go math routines behaves as one would normally expect.  This could
potentially be a problem in a mixed C/Go program for which the C part
needs to use its own definition of matherr.  Ordinarily I would expect
the C version to take precedence, in which case the Go functions may
misbehave slightly in exceptional conditions.  I don't consider this to
be a likely scenario.

Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu and
i386-pc-solaris2.11.  Committed to mainline.

Ian

diff -r 596217515a77 libgo/Makefile.am
--- a/libgo/Makefile.am	Wed Feb 08 14:20:19 2012 -0800
+++ b/libgo/Makefile.am	Wed Feb 08 14:22:22 2012 -0800
@@ -422,6 +422,7 @@
 	runtime/go-map-index.c \
 	runtime/go-map-len.c \
 	runtime/go-map-range.c \
+	runtime/go-matherr.c \
 	runtime/go-nanotime.c \
 	runtime/go-now.c \
 	runtime/go-new-map.c \
diff -r 596217515a77 libgo/configure.ac
--- a/libgo/configure.ac	Wed Feb 08 14:20:19 2012 -0800
+++ b/libgo/configure.ac	Wed Feb 08 14:22:22 2012 -0800
@@ -478,6 +478,11 @@
 CFLAGS="$CFLAGS_hold"
 LIBS="$LIBS_hold"
 
+LIBS_hold="$LIBS"
+LIBS="$LIBS $MATH_LIBS"
+AC_CHECK_FUNCS(matherr)
+LIBS="$LIBS_hold"
+
 AC_CACHE_CHECK([for __sync_bool_compare_and_swap_4],
 [libgo_cv_func___sync_bool_compare_and_swap_4],
 [AC_LINK_IFELSE([
@@ -566,6 +571,16 @@
 STRUCT_EPOLL_EVENT_FD_OFFSET=${libgo_cv_c_epoll_event_fd_offset}
 AC_SUBST(STRUCT_EPOLL_EVENT_FD_OFFSET)
 
+dnl See if struct exception is defined in <math.h>.
+AC_CHECK_TYPE([struct exception],
+[libgo_has_struct_exception=yes],
+[libgo_has_struct_exception=no],
+[#include <math.h>])
+if test "$libgo_has_struct_exception" = "yes"; then
+  AC_DEFINE(HAVE_STRUCT_EXCEPTION, 1,
+            [Define to 1 if <math.h> defines struct exception])
+fi
+
 dnl See whether setcontext changes the value of TLS variables.
 AC_CACHE_CHECK([whether setcontext clobbers TLS variables],
 [libgo_cv_lib_setcontext_clobbers_tls],
diff -r 596217515a77 libgo/runtime/go-matherr.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgo/runtime/go-matherr.c	Wed Feb 08 14:22:22 2012 -0800
@@ -0,0 +1,88 @@
+/* go-matherr.c -- a Go version of the matherr function.
+
+   Copyright 2012 The Go Authors. All rights reserved.
+   Use of this source code is governed by a BSD-style
+   license that can be found in the LICENSE file.  */
+
+/* The gccgo version of the math library calls libc functions.  On
+   some systems, such as Solaris, those functions will call matherr on
+   exceptional conditions.  This is a version of matherr appropriate
+   for Go, one which returns the values that the Go math library
+   expects.  This is fine for pure Go programs.  For mixed Go and C
+   programs this will be problematic if the C programs themselves use
+   matherr.  Normally the C version of matherr will override this, and
+   the Go code will just have to cope.  If this turns out to be too
+   problematic we can change to run pure Go code in the math library
+   on systems that use matherr.  */
+
+#include <math.h>
+#include <stdint.h>
+
+#include "config.h"
+
+#if defined(HAVE_MATHERR) && defined(HAVE_STRUCT_EXCEPTION)
+
+#define PI 3.14159265358979323846264338327950288419716939937510582097494459
+
+int
+matherr (struct exception* e)
+{
+  const char *n;
+
+  if (e->type != DOMAIN)
+    return 0;
+
+  n = e->name;
+  if (__builtin_strcmp (n, "acos") == 0
+      || __builtin_strcmp (n, "asin") == 0)
+    e->retval = NAN;
+  else if (__builtin_strcmp (n, "atan2") == 0)
+    {
+      if (e->arg1 == 0 && e->arg2 == 0)
+	{
+	  double nz;
+
+	  nz = -0.0;
+	  if (__builtin_memcmp (&e->arg2, &nz, sizeof (double)) != 0)
+	    e->retval = e->arg1;
+	  else
+	    e->retval = copysign (PI, e->arg1);
+	}
+      else
+	return 0;
+    }
+  else if (__builtin_strcmp (n, "log") == 0
+	   || __builtin_strcmp (n, "log10") == 0)
+    e->retval = NAN;
+  else if (__builtin_strcmp (n, "pow") == 0)
+    {
+      if (e->arg1 < 0)
+	e->retval = NAN;
+      else if (e->arg1 == 0 && e->arg2 == 0)
+	e->retval = 1.0;
+      else if (e->arg1 == 0 && e->arg2 < 0)
+	{
+	  double i;
+
+	  if (modf (e->arg2, &i) == 0 && ((int64_t) i & 1) == 1)
+	    e->retval = copysign (INFINITY, e->arg1);
+	  else
+	    e->retval = INFINITY;
+	}
+      else
+	return 0;
+    }
+  else if (__builtin_strcmp (n, "sqrt") == 0)
+    {
+      if (e->arg1 < 0)
+	e->retval = NAN;
+      else
+	return 0;
+    }
+  else
+    return 0;
+
+  return 1;
+}
+
+#endif

Reply via email to