summaryrefslogtreecommitdiff
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;
+}