diff mbox

Go patch committed: Provide matherr function

Message ID mcrsjilrm16.fsf@dhcp-172-18-216-180.mtv.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor Feb. 8, 2012, 10:35 p.m. UTC
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

Comments

Rainer Orth Feb. 9, 2012, 1:37 p.m. UTC | #1
Ian Lance Taylor <iant@google.com> writes:

> 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.

Unfortunately, this breaks the libgo build on Solaris 8 and 9:

/vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c: In function 'matherr':
/vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:38:17: error: 'NAN' undeclared (first use in this function)
/vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:38:17: note: each undeclared identifier is reported only once for each function it appears in
/vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:68:28: error: 'INFINITY' undeclared (first use in this function)
make[4]: *** [go-matherr.lo] Error 1

I couldn't find an appproprite replacement in the system headers.

	Rainer
Ian Lance Taylor Feb. 9, 2012, 5:53 p.m. UTC | #2
Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> writes:

> Ian Lance Taylor <iant@google.com> writes:
>
>> 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.
>
> Unfortunately, this breaks the libgo build on Solaris 8 and 9:
>
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c: In function 'matherr':
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:38:17: error: 'NAN' undeclared (first use in this function)
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:38:17: note: each undeclared identifier is reported only once for each function it appears in
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:68:28: error: 'INFINITY' undeclared (first use in this function)
> make[4]: *** [go-matherr.lo] Error 1
>
> I couldn't find an appproprite replacement in the system headers.

Hmmm, INFINITY and NAN are in C99.

Does it work to do something like

#ifndef INFINITY
#define INFINITY (1.0 / 0.0)
#endif

#ifndef NAN
#define NAN (0.0 / 0.0)
#endif

?

Another possibility for NAN would to call nan(), if that is available.

Ian
Ian Lance Taylor Feb. 9, 2012, 5:55 p.m. UTC | #3
Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> writes:

> Ian Lance Taylor <iant@google.com> writes:
>
>> 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.
>
> Unfortunately, this breaks the libgo build on Solaris 8 and 9:
>
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c: In function 'matherr':
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:38:17: error: 'NAN' undeclared (first use in this function)
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:38:17: note: each undeclared identifier is reported only once for each function it appears in
> /vol/gcc/src/hg/trunk/local/libgo/runtime/go-matherr.c:68:28: error: 'INFINITY' undeclared (first use in this function)
> make[4]: *** [go-matherr.lo] Error 1
>
> I couldn't find an appproprite replacement in the system headers.

Oh, wait, we can use __builtin_inf and __builtin_nan.

Ian
diff mbox

Patch

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