about summary refs log tree commit diff
path: root/math
diff options
context:
space:
mode:
Diffstat (limited to 'math')
-rw-r--r--math/Makefile6
-rw-r--r--math/atest-exp2.c240
-rw-r--r--math/libm-test.c3
-rw-r--r--math/math_private.h3
-rw-r--r--math/test-reduce.c207
5 files changed, 457 insertions, 2 deletions
diff --git a/math/Makefile b/math/Makefile
index 62619111c9..bc54d57c43 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -51,7 +51,7 @@ libm-calls = e_acos e_acosh e_asin e_atan2 e_atanh e_cosh e_exp e_fmod	\
 	     s_floor s_frexp s_ilogb s_ldexp s_log1p s_logb 		\
 	     s_modf s_nextafter s_rint s_scalbn s_significand		\
 	     s_sin s_tan s_tanh w_acos w_acosh w_asin			\
-	     w_atan2 w_atanh w_cosh w_drem w_exp w_fmod w_gamma		\
+	     w_atan2 w_atanh w_cosh w_drem w_exp w_exp2 w_fmod w_gamma	\
 	     w_hypot w_j0 w_j1 w_jn w_lgamma w_lgamma_r			\
 	     w_log w_log10 w_pow w_remainder w_scalb w_sinh w_sqrt	\
 	     s_signbit s_fpclassify s_fmax s_fmin s_fdim s_nan s_trunc	\
@@ -78,7 +78,7 @@ distribute += $(long-c-yes:=.c)
 # Rules for the test suite.
 tests = test-float test-double $(test-longdouble-$(long-double-fcts)) \
 	test-ifloat test-idouble test-matherr test-fenv \
-	atest-exp atest-sincos
+	atest-exp atest-sincos atest-exp2 # test-reduce
 # We do the `long double' tests only if this data type is available and
 # distrinct from `double'.
 test-longdouble-yes = test-ldouble test-ildoubl
@@ -93,8 +93,10 @@ LDLIBS-test-float = math/libm
 LDLIBS-test-double = math/libm
 LDLIBS-test-ldouble = math/libm
 LDLIBS-test-matherr = math/libm
+LDLIBS-test-reduce = math/libm
 LDLIBS-atest-exp = math/libm
 LDLIBS-atest-sincos = math/libm
+LDLIBS-atest-exp2 = math/libm
 
 distribute += libm-test.c
 
diff --git a/math/atest-exp2.c b/math/atest-exp2.c
new file mode 100644
index 0000000000..95e72aa507
--- /dev/null
+++ b/math/atest-exp2.c
@@ -0,0 +1,240 @@
+/* Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Geoffrey Keating <Geoff.Keating@anu.edu.au>, 1997.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <stdio.h>
+#include <math.h>
+#include <gmp.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#define PRINT_ERRORS 0
+
+#define TOL 80
+#define N2 18
+#define FRAC (32*4)
+
+#define mpbpl (CHAR_BIT * sizeof (mp_limb_t))
+#define SZ (FRAC / mpbpl + 1)
+typedef mp_limb_t mp1[SZ], mp2[SZ * 2];
+
+/* This string has 101 hex digits.  */
+static const char exp1[102] = "2" /* point */
+"b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a7"
+"84d9045190cfef324e7738926cfbe5f4bf8d8d8c31d763da07";
+static const char exp_m1[102] = "0" /* point */
+"5e2d58d8b3bcdf1abadec7829054f90dda9805aab56c773330"
+"24b9d0a507daedb16400bf472b4215b8245b669d90d27a5aea";
+
+static const char hexdig[] = "0123456789abcdef";
+
+void
+print_mpn_fp (const mp_limb_t *x, unsigned int dp, unsigned int base)
+{
+   unsigned int i;
+   mp1 tx;
+
+   memcpy (tx, x, sizeof (mp1));
+   if (base == 16)
+     fputs ("0x", stdout);
+   assert (x[SZ-1] < base);
+   fputc (hexdig[x[SZ - 1]], stdout);
+   fputc ('.', stdout);
+   for (i = 0; i < dp; i++)
+     {
+       tx[SZ - 1] = 0;
+       mpn_mul_1 (tx, tx, SZ, base);
+       assert (tx[SZ - 1] < base);
+       fputc (hexdig[tx[SZ - 1]], stdout);
+     }
+}
+
+void
+read_mpn_hex(mp_limb_t *x, const char *str)
+{
+  int i;
+
+  memset (x, 0, sizeof (mp1));
+  for (i = -1; i < 100 && i < FRAC / 4; ++i)
+    x[(FRAC - i * 4 - 4) / mpbpl] |= (strchr (hexdig, str[i + 1]) - hexdig
+				      << (FRAC - i * 4 - 4) % mpbpl);
+}
+
+static mp_limb_t *get_log2(void) __attribute__((const));
+static mp_limb_t *
+get_log2(void)
+{
+  static mp1 log2_m;
+  static int log2_m_inited = 0;
+  static const char log2[102] = "0" /* point */
+    "b17217f7d1cf79abc9e3b39803f2f6af40f343267298b62d8a"
+    "0d175b8baafa2be7b876206debac98559552fb4afa1b10ed2e";
+
+  if (!log2_m_inited)
+    {
+      read_mpn_hex (log2_m, log2);
+      log2_m_inited = 1;
+    }
+  return log2_m;
+}
+
+/* Compute e^x.  */
+void
+exp_mpn (mp1 ex, mp1 x)
+{
+   unsigned int n;
+   mp1 xp;
+   mp2 tmp;
+   mp_limb_t chk, round;
+   mp1 tol;
+
+   memset (xp, 0, sizeof (mp1));
+   memset (ex, 0, sizeof (mp1));
+   xp[FRAC / mpbpl] = 1 << FRAC % mpbpl;
+   memset (tol, 0, sizeof (mp1));
+   tol[(FRAC - TOL) / mpbpl] = 1 << (FRAC - TOL) % mpbpl;
+
+   n = 0;
+
+   do
+     {
+       /* Calculate sum(x^n/n!) until the next term is sufficiently small.  */
+
+       mpn_mul_n (tmp, xp, x, SZ);
+       assert(tmp[SZ * 2 - 1] == 0);
+       if (n > 0)
+	 round = mpn_divmod_1 (xp, tmp + FRAC / mpbpl, SZ, n);
+       chk = mpn_add_n (ex, ex, xp, SZ);
+       assert (chk == 0);
+       ++n;
+       assert (n < 80); /* Catch too-high TOL.  */
+     }
+   while (n < 10 || mpn_cmp (xp, tol, SZ) >= 0);
+}
+
+/* Calculate 2^x.  */
+void
+exp2_mpn (mp1 ex, mp1 x)
+{
+  mp2 tmp;
+  mpn_mul_n (tmp, x, get_log2 (), SZ);
+  assert(tmp[SZ * 2 - 1] == 0);
+  exp_mpn (ex, tmp + FRAC / mpbpl);
+}
+
+
+static int
+mpn_bitsize(const mp_limb_t *SRC_PTR, mp_size_t SIZE)
+{
+  int i, j;
+  for (i = SIZE - 1; i > 0; --i)
+    if (SRC_PTR[i] != 0)
+      break;
+  for (j = mpbpl - 1; j > 0; --j)
+    if ((SRC_PTR[i] & 1 << j) != 0)
+      break;
+
+  return i * 32 + j;
+}
+
+int
+main (void)
+{
+  mp1 ex, x, xt, e2, e3;
+  int i;
+  int errors = 0;
+  int failures = 0;
+  mp1 maxerror;
+  int maxerror_s = 0;
+  const double sf = pow (2, mpbpl);
+
+  /* assert(mpbpl == mp_bits_per_limb); */
+  assert(FRAC / mpbpl * mpbpl == FRAC);
+
+  memset (maxerror, 0, sizeof (mp1));
+  memset (xt, 0, sizeof (mp1));
+  xt[(FRAC - N2) / mpbpl] = 1 << (FRAC - N2) % mpbpl;
+
+  for (i = 0; i < (1 << N2); ++i)
+    {
+      int e2s, e3s, j;
+      double de2;
+
+      mpn_mul_1 (x, xt, SZ, i);
+      exp2_mpn (ex, x);
+      de2 = exp2 (i / (double) (1 << N2));
+      for (j = SZ - 1; j >= 0; --j)
+	{
+	  e2[j] = (mp_limb_t) de2;
+	  de2 = (de2 - e2[j]) * sf;
+	}
+      if (mpn_cmp (ex, e2, SZ) >= 0)
+	mpn_sub_n (e3, ex, e2, SZ);
+      else
+	mpn_sub_n (e3, e2, ex, SZ);
+
+      e2s = mpn_bitsize (e2, SZ);
+      e3s = mpn_bitsize (e3, SZ);
+      if (e3s > 1 && e2s - e3s < 54)
+	{
+#if PRINT_ERRORS
+	  printf ("%06x ", i * (0x100000 / (1 << N2)));
+	  print_mpn_fp (ex, (FRAC / 4) + 1, 16);
+	  putchar ('\n');
+	  fputs ("       ",stdout);
+	  print_mpn_fp (e2, (FRAC / 4) + 1, 16);
+	  putchar ('\n');
+	  printf (" %c     ",
+		  e2s - e3s < 54 ? e2s - e3s == 53 ? 'e' : 'F' : 'P');
+	  print_mpn_fp (e3, (FRAC / 4) + 1, 16);
+	  putchar ('\n');
+#endif
+	  errors += (e2s - e3s == 53);
+	  failures += (e2s - e3s < 53);
+	}
+      if (e3s >= maxerror_s
+	  && mpn_cmp (e3, maxerror, SZ) > 0)
+	{
+	  memcpy (maxerror, e3, sizeof (mp1));
+	  maxerror_s = e3s;
+	}
+    }
+
+  /* Check exp_mpn against precomputed value of exp(1).  */
+  memset (x, 0, sizeof (mp1));
+  x[FRAC / mpbpl] = 1 << FRAC % mpbpl;
+  exp_mpn (ex, x);
+  read_mpn_hex (e2, exp1);
+  if (mpn_cmp (ex, e2, SZ) >= 0)
+    mpn_sub_n (e3, ex, e2, SZ);
+  else
+    mpn_sub_n (e3, e2, ex, SZ);
+
+  printf ("%d failures; %d errors; error rate %0.2f%%\n", failures, errors,
+	  errors * 100.0 / (double) (1 << N2));
+  fputs ("maximum error:   ", stdout);
+  print_mpn_fp (maxerror, (FRAC / 4) + 1, 16);
+  putchar ('\n');
+  fputs ("error in exp(1): ", stdout);
+  print_mpn_fp (e3, (FRAC / 4) + 1, 16);
+  putchar ('\n');
+
+  return failures == 0 ? 0 : 1;
+}
diff --git a/math/libm-test.c b/math/libm-test.c
index da1de8385f..2075adcb59 100644
--- a/math/libm-test.c
+++ b/math/libm-test.c
@@ -1114,6 +1114,9 @@ exp2_test (void)
   check_isinfp ("exp2 (+inf) == +inf", FUNC(exp2) (plus_infty));
   check ("exp2 (-inf) == 0", FUNC(exp2) (minus_infty), 0);
   check ("exp2 (10) == 1024", FUNC(exp2) (10), 1024);
+  check ("exp2 (-1) == 0.5", FUNC(exp2) (-1), 0.5);
+  check_isinfp ("exp2 (1e6) == +inf", FUNC(exp2) (1e6));
+  check ("exp2 (-1e6) == 0", FUNC(exp2) (-1e6), 0);
 }
 
 
diff --git a/math/math_private.h b/math/math_private.h
index 74b729d419..4b3b5be3bf 100644
--- a/math/math_private.h
+++ b/math/math_private.h
@@ -252,6 +252,7 @@ extern double __ieee754_atanh __P((double));
 extern double __ieee754_asin __P((double));
 extern double __ieee754_atan2 __P((double,double));
 extern double __ieee754_exp __P((double));
+extern double __ieee754_exp2 __P((double));
 extern double __ieee754_cosh __P((double));
 extern double __ieee754_fmod __P((double,double));
 extern double __ieee754_pow __P((double,double));
@@ -290,6 +291,7 @@ extern float __ieee754_atanhf __P((float));
 extern float __ieee754_asinf __P((float));
 extern float __ieee754_atan2f __P((float,float));
 extern float __ieee754_expf __P((float));
+extern float __ieee754_exp2f __P((float));
 extern float __ieee754_coshf __P((float));
 extern float __ieee754_fmodf __P((float,float));
 extern float __ieee754_powf __P((float,float));
@@ -327,6 +329,7 @@ extern long double __ieee754_atanhl __P((long double));
 extern long double __ieee754_asinl __P((long double));
 extern long double __ieee754_atan2l __P((long double,long double));
 extern long double __ieee754_expl __P((long double));
+extern long double __ieee754_exp2l __P((long double));
 extern long double __ieee754_coshl __P((long double));
 extern long double __ieee754_fmodl __P((long double,long double));
 extern long double __ieee754_powl __P((long double,long double));
diff --git a/math/test-reduce.c b/math/test-reduce.c
new file mode 100644
index 0000000000..5149ead341
--- /dev/null
+++ b/math/test-reduce.c
@@ -0,0 +1,207 @@
+/* Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Geoffrey Keating <Geoff.Keating@anu.edu.au>, 1997.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* This is a generic program for comparing two precisions of a one-input
+   mathematical function.  It is amazingly good at detecting when GCC
+   folds constants improperly.  */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <math.h>
+#include <ieee754.h>
+#include <fenv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#define TSMALL float
+#define RSMALL(rfun) ({ unsigned rnum = (rfun); *(float *) &rnum; })
+#define TBIG double
+#define XDIFF (24)
+#define REDUCE(x) \
+   ({ union ieee754_float u = { x }; u.ieee.exponent = 0x80; x = u.f; })
+#define ABS(x) fabs(x)
+
+#define string_0(x) #x
+#define string_1(x) string_0(x)
+#define TBIG_NAME string_1(TBIG)
+#define TSMALL_NAME string_1(TSMALL)
+
+#define R_NEAREST 1
+#define R_ZERO 2
+#define R_UP 4
+#define R_DOWN 8
+#define R_ALL (R_NEAREST|R_ZERO|R_UP|R_DOWN)
+static fenv_t rmodes[4];
+static const char * const rmnames[4] =
+{ "near","zero","+Inf","-Inf" };
+
+static int quiet = 0;
+
+#ifdef FE_ALL_INVALID
+static const int invalid_exceptions = (FE_ALL_INVALID
+				       | FE_INVALID | FE_DIVBYZERO);
+#else
+static const int invalid_exceptions = (FE_INVALID | FE_DIVBYZERO);
+#endif
+
+static int
+checkit (char *fname,
+	 TSMALL (*fsmall) (TSMALL), TBIG (*fbig) (TBIG),
+	 unsigned smalltries, unsigned largetries)
+{
+  unsigned int i, nerrors = 0, nwarn;
+
+  int tryone (TSMALL fval)
+    {
+      int rmode;
+      int excepts, exceptsb;
+      TSMALL fres;
+      TBIG fvalb, fresb, diff;
+      char warn;
+
+      fvalb = (TBIG) fval;
+
+      for (rmode = 0; rmode < 4; ++rmode)
+	{
+	  fesetenv (rmodes + rmode);
+	  fres = fsmall (fval);
+	  excepts = fetestexcept (invalid_exceptions);
+	  fesetenv (rmodes + rmode);
+	  fresb = fbig (fvalb);
+	  exceptsb = fetestexcept (invalid_exceptions);
+
+	  if (excepts != exceptsb)
+	    {
+	      unsigned char *fvp = (unsigned char *) &fval;
+	      size_t j;
+
+	      printf ("%s(", fname);
+	      for (j = 0; j < sizeof (TSMALL); j++)
+		printf ("%02x", fvp[j]);
+	      printf ("),%s: exceptions %s: %08x, %s: %08x\n",
+		      rmnames[rmode],
+		      TBIG_NAME, exceptsb, TSMALL_NAME, excepts);
+	      if (++nerrors > 10)
+		return 1;
+	    }
+
+	  diff = ABS (fres - (TSMALL) fresb);
+	  if (fres == (TSMALL) fresb
+	      || isnan (fres) && isnan (fresb)
+	      || logb (fresb) - logb (diff) < XDIFF - 1)
+	    continue;
+
+	  if (logb (fresb) - logb (diff) < XDIFF)
+	    {
+	      if (++nwarn > 10 || quiet)
+		continue;
+	      warn = 'w';
+	    }
+	  else
+	    {
+	      if (++nerrors > 10)
+		return 1;
+	      warn = 'e';
+	    }
+
+	  {
+	    TSMALL fresbs = (TSMALL) fresb;
+	    unsigned char *fvp = (unsigned char *) &fval;
+	    unsigned char *frp = (unsigned char *) &fres;
+	    unsigned char *frpb = (unsigned char *) &fresb;
+	    unsigned char *frpbs = (unsigned char *) &fresbs;
+	    size_t j;
+
+	    printf ("%s(",fname);
+	    for (j = 0; j < sizeof (TSMALL); ++j)
+	      printf ("%02x", fvp[j]);
+	    printf ("),%s: %s ", rmnames[rmode], TBIG_NAME);
+	    for (j = 0; j < sizeof (TBIG); ++j)
+	      printf ("%02x", frpb[j]);
+	    printf (" (");
+	    for (j = 0; j < sizeof (TSMALL); ++j)
+	      printf ("%02x", frpbs[j]);
+	    printf ("), %s ", TSMALL_NAME);
+	    for (j = 0; j < sizeof (TSMALL); ++j)
+	      printf ("%02x", frp[j]);
+	    printf (" %c\n", warn);
+	  }
+	}
+      return 0;
+    }
+
+  nwarn = 0;
+  for (i = 0; i < smalltries; i++)
+    {
+      TSMALL fval;
+
+      fval = RSMALL (rand () ^ rand () << 16);
+      REDUCE (fval);
+      if (tryone (fval))
+	break;
+    }
+
+  printf ("%s-small: %d errors, %d (%0.2f%%) inaccuracies\n",
+	  fname, nerrors, nwarn,
+	  nwarn * 0.25 / ((double) smalltries));
+
+  nwarn = 0;
+  for (i = 0; i < largetries; ++i)
+    {
+      TSMALL fval;
+
+      fval = RSMALL (rand () ^ rand () << 16);
+      if (tryone (fval))
+	break;
+    }
+
+  printf ("%s-large: %d errors, %d (%0.2f%%) inaccuracies\n",
+	  fname, nerrors, nwarn,
+	  nwarn * 0.25 / ((double) largetries));
+  return nerrors == 0;
+}
+
+int
+main (void)
+{
+  int r;
+
+  _LIB_VERSION = _IEEE_;
+
+  /* Set up environments for rounding modes.  */
+  fesetenv (FE_DFL_ENV);
+  fesetround (FE_TONEAREST);
+  fegetenv (rmodes + 0);
+  fesetround (FE_TOWARDSZERO);
+  fegetenv (rmodes + 1);
+  fesetround (FE_UPWARD);
+  fegetenv (rmodes + 2);
+  fesetround (FE_DOWNWARD);
+  fegetenv (rmodes + 3);
+
+  /* Seed the RNG.  */
+  srand (time (0));
+
+  /* Do it.  */
+  r  = checkit ("exp2", exp2f, exp2, 1 << 20, 1 << 15);
+  r &= checkit ("exp",  expf,  exp,  1 << 20, 1 << 15);
+  return r ? 0 : 1;
+}