summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am3
-rw-r--r--tools/mpcheck/Makefile.am31
-rw-r--r--tools/mpcheck/README3
-rw-r--r--tools/mpcheck/mpcheck-common.c193
-rw-r--r--tools/mpcheck/mpcheck-double.c242
-rw-r--r--tools/mpcheck/mpcheck-float.c247
-rw-r--r--tools/mpcheck/mpcheck-float128.c252
-rw-r--r--tools/mpcheck/mpcheck-longdouble.c235
-rw-r--r--tools/mpcheck/mpcheck-template1.c155
-rw-r--r--tools/mpcheck/mpcheck-template2.c130
-rw-r--r--tools/mpcheck/mpcheck-template3.c172
11 files changed, 1662 insertions, 1 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 5730d54..86257e1 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,6 +1,7 @@
## tools/Makefile.am -- Process this file with automake to produce Makefile.in
##
## Copyright (C) 2014 CNRS
+## Copyright (C) 2020 INRIA
##
## This file is part of GNU MPC.
##
@@ -17,5 +18,5 @@
## You should have received a copy of the GNU Lesser General Public License
## along with this program. If not, see http://www.gnu.org/licenses/ .
-SUBDIRS = bench
+SUBDIRS = bench mpcheck
diff --git a/tools/mpcheck/Makefile.am b/tools/mpcheck/Makefile.am
new file mode 100644
index 0000000..77ed6b0
--- /dev/null
+++ b/tools/mpcheck/Makefile.am
@@ -0,0 +1,31 @@
+## tools/mpcheck/Makefile.am -- Process this file with automake to produce Makefile.in
+##
+## Copyright (C) 2014 CNRS
+## Copyright (C) 2020 INRIA
+##
+## This file is part of GNU MPC.
+##
+## GNU MPC is free software; you can redistribute it and/or modify it under
+## the terms of the GNU Lesser General Public License as published by the
+## Free Software Foundation; either version 3 of the License, or (at your
+## option) any later version.
+##
+## GNU MPC 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 Lesser General Public License for
+## more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this program. If not, see http://www.gnu.org/licenses/ .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src
+AM_DEFAULT_SOURCE_EXT = .c
+
+LDADD = $(top_builddir)/src/libmpc.la -lm
+
+BINARIES = mpcheck-float mpcheck-double mpcheck-longdouble mpcheck-float128
+EXTRA_PROGRAMS = $(BINARIES)
+CLEANFILES = $(BINARIES)
+
+mpcheck : $(BINARIES)
+
diff --git a/tools/mpcheck/README b/tools/mpcheck/README
new file mode 100644
index 0000000..f35ff80
--- /dev/null
+++ b/tools/mpcheck/README
@@ -0,0 +1,3 @@
+The mpcheck tool provides tests to check the MPC library against the
+C library. It is meant as a tool for developers.
+
diff --git a/tools/mpcheck/mpcheck-common.c b/tools/mpcheck/mpcheck-common.c
new file mode 100644
index 0000000..0133f2a
--- /dev/null
+++ b/tools/mpcheck/mpcheck-common.c
@@ -0,0 +1,193 @@
+/* shared routines for mpcheck-xxx
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+#include <sys/types.h>
+#include <sys/resource.h>
+
+unsigned long Max_err_re = 0, Max_err_im = 0;
+unsigned long Max_err = 0; /* for real-valued functions like abs */
+int recheck = 0;
+
+/* y is the reference value computed by MPC */
+static unsigned long
+ulp_error (mpfr_t x, mpfr_t y)
+{
+ mpfr_t z;
+ mpfr_prec_t p = mpfr_get_prec (y);
+ unsigned long n, special = 0;
+ mpfr_exp_t e;
+
+ if (mpfr_cmp (x, y) == 0)
+ return 0;
+
+ assert (!mpfr_nan_p (x));
+ assert (!mpfr_nan_p (y));
+
+ if (mpfr_inf_p (x) || mpfr_inf_p (y))
+ {
+ if (mpfr_number_p (x) && mpfr_inf_p (y))
+ {
+ /* only MPFR returns Inf: we replace y by the maximal
+ floating-point number */
+ if (mpfr_sgn (y) > 0) /* +Inf */
+ mpfr_nextbelow (y);
+ else
+ mpfr_nextabove (y);
+ special = 1;
+ }
+ else
+ {
+ /* only glibc returns Inf */
+ if (mpfr_sgn (x) > 0) /* +Inf */
+ mpfr_nextbelow (x);
+ else
+ mpfr_nextabove (x);
+ special = 2;
+ }
+ }
+
+ mpfr_init2 (z, p);
+ mpfr_sub (z, x, y, MPFR_RNDN);
+ mpfr_abs (z, z, MPFR_RNDN);
+ /* divide by ulp(y) = 2^(EXP(y) - p) */
+ e = mpfr_get_exp (y) - p;
+ /* take into account subnormals: smallest number is 2^(emin-1) */
+ if (e < emin - 1)
+ e = emin - 1;
+ mpfr_div_2si (z, z, e, MPFR_RNDN);
+ n = mpfr_get_ui (z, MPFR_RNDZ);
+ mpfr_clear (z);
+ if (special == 1) /* restore original y */
+ {
+ if (mpfr_sgn (y) > 0)
+ mpfr_nextabove (y);
+ else
+ mpfr_nextbelow (y);
+ }
+ if (special == 2) /* restore original x */
+ {
+ if (mpfr_sgn (x) > 0)
+ mpfr_nextabove (x);
+ else
+ mpfr_nextbelow (x);
+ }
+ if (special == 0)
+ return n;
+ else
+ return (n < ULONG_MAX) ? n + 1 : n;
+}
+
+static void
+mpcheck_random (mpc_t z)
+{
+ unsigned long erange;
+
+ mpc_urandom (z, state);
+
+ /* set sign and exponent of the real part */
+ mpz_set_ui (expz, 2 * (emax - emin + 1));
+ mpz_urandomm (expz, state, expz);
+ erange = mpz_get_ui (expz);
+ /* bit 0 of erange gives the sign */
+ if (erange & 1)
+ mpfr_neg (mpc_realref (z), mpc_realref (z), MPFR_RNDN);
+ mpfr_set_exp (mpc_realref (z), emin + (erange >> 1));
+ /* make sure the numbers are representable in IEEE format */
+ mpfr_subnormalize (mpc_realref (z), 0, MPFR_RNDN);
+
+ /* set sign and exponent of the imaginary part */
+ mpz_set_ui (expz, 2 * (emax - emin + 1));
+ mpz_urandomm (expz, state, expz);
+ erange = mpz_get_ui (expz);
+ /* bit 0 of erange gives the sign */
+ if (erange & 1)
+ mpfr_neg (mpc_imagref (z), mpc_imagref (z), MPFR_RNDN);
+ mpfr_set_exp (mpc_imagref (z), emin + (erange >> 1));
+ /* make sure the numbers are representable in IEEE format */
+ mpfr_subnormalize (mpc_imagref (z), 0, MPFR_RNDN);
+}
+
+/* y is the reference value computed with MPC:
+ * return 0 if x and y are equal (including both NaN or Inf)
+ * return 1 if x and y disagree (neither NaN nor Inf)
+ * return 2 if x is NaN but not y
+ * return 4 if x and y are infinities of different signs
+ */
+static int
+mpcheck_mpfr_cmp (mpfr_t x, mpfr_t y)
+{
+ if (mpfr_number_p (x) && mpfr_number_p (y))
+ return mpfr_cmp (x, y);
+ /* now either x or y is NaN or Inf */
+ if (mpfr_nan_p (x) || mpfr_nan_p (y))
+ {
+ if (mpfr_nan_p (x) && mpfr_nan_p (y))
+ return 0;
+ if (mpfr_nan_p (x))
+ return 2;
+ fprintf (stderr, "Error, NaN and not NaN\n");
+ exit (1);
+ }
+ /* now either x or y is Inf */
+ if (mpfr_inf_p (x) && mpfr_inf_p (y))
+ {
+ if (mpfr_sgn (x) * mpfr_sgn (y) > 0)
+ return 0;
+ return 4;
+ }
+ return 1; /* Inf and non-Inf: differ, will be treated by ulp_error */
+}
+
+/* y is the reference value computed with MPC */
+static int
+mpcheck_mpc_cmp (mpc_t x, mpc_t y)
+{
+ return mpcheck_mpfr_cmp (mpc_realref (x), mpc_realref (y)) |
+ mpcheck_mpfr_cmp (mpc_imagref (x), mpc_imagref (y));
+}
+
+static void
+report_maximal_errors (void)
+{
+ printf ("Maximal errors: %lu (real), %lu (imag), %lu (abs/arg)\n",
+ Max_err_re, Max_err_im, Max_err);
+}
+
+/* return non-zero if both a and b are NaN, or if both are not NaN
+ and are equal */
+static int
+mpfr_agree (mpfr_t a, mpfr_t b, int inex)
+{
+ if (mpfr_nan_p (a) || mpfr_nan_p (b))
+ return mpfr_nan_p (a) && mpfr_nan_p (b);
+ else
+ /* in the case of even rounding, we can't tell for sure */
+ return mpfr_equal_p (a, b) || inex == 2 || inex == -2;
+}
+
+static int
+cputime (void)
+{
+ struct rusage rus;
+
+ getrusage (0, &rus);
+ return rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000;
+}
+
diff --git a/tools/mpcheck/mpcheck-double.c b/tools/mpcheck/mpcheck-double.c
new file mode 100644
index 0000000..df216aa
--- /dev/null
+++ b/tools/mpcheck/mpcheck-double.c
@@ -0,0 +1,242 @@
+/* mpcheck-double -- compare mpc functions against "double complex"
+ from the GNU libc implementation
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+/* the GNU libc provides the following functions (as of 2.31),
+ with 'f' suffix for the float/binary32 version, with no suffix
+ for the double/binary64 version, with 'l' suffix for the long double
+ version, and with 'f128' suffix for the __float128 version:
+
+ cabs casinh cexp csinh
+ cacos catan clog csqrt
+ cacosh catanh clog10 ctan
+ carg ccos cpow ctanh
+ casin ccosh csin
+*/
+
+#define _GNU_SOURCE /* for clog10 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <complex.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#ifdef __GNUC__
+#include <gnu/libc-version.h>
+#endif
+#include "mpc.h"
+
+#define PRECISION 53
+#define EMAX 1024
+#define TYPE double
+#define SUFFIX
+
+#define mpc_get_type mpc_get_dc
+#define mpc_set_type mpc_set_dc
+#define mpfr_set_type mpfr_set_d
+
+gmp_randstate_t state;
+mpz_t expz; /* global variable used in mpcheck_random */
+unsigned long seed = 0;
+int verbose = 0;
+mpfr_exp_t emin, emax;
+
+#include "mpcheck-common.c"
+
+#define FOO add
+#define CFOO(x,y) (x+y)
+#include "mpcheck-template3.c"
+
+#define FOO sub
+#define CFOO(x,y) (x-y)
+#include "mpcheck-template3.c"
+
+#define FOO mul
+#define CFOO(x,y) (x*y)
+#include "mpcheck-template3.c"
+
+#define FOO div
+#define CFOO(x,y) (x/y)
+#include "mpcheck-template3.c"
+
+#define FOO pow
+#include "mpcheck-template3.c"
+
+#define FOO abs
+#include "mpcheck-template2.c"
+
+#define FOO arg
+#include "mpcheck-template2.c"
+
+#define FOO sqrt
+#include "mpcheck-template1.c"
+
+#define FOO acos
+#include "mpcheck-template1.c"
+
+#define FOO acosh
+#include "mpcheck-template1.c"
+
+#define FOO asin
+#include "mpcheck-template1.c"
+
+#define FOO asinh
+#include "mpcheck-template1.c"
+
+#define FOO atan
+#include "mpcheck-template1.c"
+
+#define FOO atanh
+#include "mpcheck-template1.c"
+
+#define FOO cos
+#include "mpcheck-template1.c"
+
+#define FOO cosh
+#include "mpcheck-template1.c"
+
+#define FOO exp
+#include "mpcheck-template1.c"
+
+#define FOO log
+#include "mpcheck-template1.c"
+
+#define FOO log10
+#include "mpcheck-template1.c"
+
+#define FOO sin
+#include "mpcheck-template1.c"
+
+#define FOO sinh
+#include "mpcheck-template1.c"
+
+/* use reduced exponent range for tan and tanh */
+#define FOO_EMIN -8
+#define FOO_EMAX 8
+
+#define FOO tan
+#include "mpcheck-template1.c"
+
+#define FOO tanh
+#include "mpcheck-template1.c"
+
+#undef FOO_EMIN
+#undef FOO_EMAX
+
+int
+main (int argc, char *argv[])
+{
+ mpfr_prec_t p = PRECISION; /* precision of 'double' */
+ unsigned long n = 1000000; /* default number of random tests per function */
+
+ while (argc >= 2 && argv[1][0] == '-')
+ {
+ if (argc >= 3 && strcmp (argv[1], "-p") == 0)
+ {
+ p = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-seed") == 0)
+ {
+ seed = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-num") == 0)
+ {
+ n = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (strcmp (argv[1], "-v") == 0)
+ {
+ verbose ++;
+ argc --;
+ argv ++;
+ }
+ else if (strcmp (argv[1], "-check") == 0)
+ {
+ recheck = 1;
+ argc --;
+ argv ++;
+ }
+ else
+ {
+ fprintf (stderr, "Unknown option %s\n", argv[1]);
+ exit (1);
+ }
+ }
+
+ /* set exponent range */
+ emin = -EMAX - PRECISION + 4; /* should be -1073 */
+ emax = EMAX;
+ mpfr_set_emin (emin);
+ mpfr_set_emax (emax);
+
+ gmp_randinit_default (state);
+ mpz_init (expz);
+
+#ifdef __GNUC__
+ printf ("GNU libc version: %s\n", gnu_get_libc_version ());
+ printf ("GNU libc release: %s\n", gnu_get_libc_release ());
+#endif
+
+ if (seed == 0)
+ seed = getpid ();
+ printf ("Using random seed %lu\n", seed);
+
+ /* (complex,complex) -> complex */
+ test_add (p, n);
+ test_sub (p, n);
+ test_mul (p, n);
+ test_div (p, n);
+ test_pow (p, n);
+
+ /* complex -> real */
+ test_abs (p, n);
+ test_arg (p, n);
+
+ /* complex -> complex */
+ test_sqrt (p, n);
+ test_acos (p, n);
+ test_acosh (p, n);
+ test_asin (p, n);
+ test_asinh (p, n);
+ test_atan (p, n);
+ test_atanh (p, n);
+ test_cos (p, n);
+ test_cosh (p, n);
+ test_exp (p, n);
+ test_log (p, n);
+ test_log10 (p, n);
+ test_sin (p, n);
+ test_sinh (p, n);
+ test_tan (p, n);
+ test_tanh (p, n);
+
+ gmp_randclear (state);
+ mpz_clear (expz);
+
+ report_maximal_errors ();
+
+ return 0;
+}
diff --git a/tools/mpcheck/mpcheck-float.c b/tools/mpcheck/mpcheck-float.c
new file mode 100644
index 0000000..9e4bdea
--- /dev/null
+++ b/tools/mpcheck/mpcheck-float.c
@@ -0,0 +1,247 @@
+/* mpcheck-float -- compare mpc functions against "float complex"
+ from the GNU libc implementation
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+/* the GNU libc provides the following functions (as of 2.31),
+ with 'f' suffix for the float/binary32 version, with no suffix
+ for the double/binary64 version, with 'l' suffix for the long double
+ version, and with 'f128' suffix for the __float128 version:
+
+ cabs casinh cexp csinh
+ cacos catan clog csqrt
+ cacosh catanh clog10 ctan
+ carg ccos cpow ctanh
+ casin ccosh csin
+*/
+
+#define _GNU_SOURCE /* for clog10 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <complex.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include "mpc.h"
+#ifdef __GNUC__
+#include <gnu/libc-version.h>
+#endif
+
+#define PRECISION 24
+#define EMAX 128
+#define TYPE float
+#define SUFFIX f
+
+#define mpfr_set_type mpfr_set_flt
+
+static TYPE complex
+mpc_get_type (mpc_t x, mpc_rnd_t rnd)
+{
+ /* there is no mpc_get_fltc function */
+ return (TYPE complex) mpc_get_dc (x, rnd);
+}
+
+static int
+mpc_set_type (mpc_t x, TYPE complex y, mpc_rnd_t rnd)
+{
+ /* there is no mpc_set_fltc function */
+ return mpc_set_dc (x, (double complex) y, rnd);
+}
+
+gmp_randstate_t state;
+mpz_t expz; /* global variable used in mpcheck_random */
+unsigned long seed = 0;
+int verbose = 0;
+mpfr_exp_t emin, emax;
+
+#include "mpcheck-common.c"
+
+#define FOO add
+#define CFOO(x,y) (x+y)
+#include "mpcheck-template3.c"
+
+#define FOO sub
+#define CFOO(x,y) (x-y)
+#include "mpcheck-template3.c"
+
+#define FOO mul
+#define CFOO(x,y) (x*y)
+#include "mpcheck-template3.c"
+
+#define FOO div
+#define CFOO(x,y) (x/y)
+#include "mpcheck-template3.c"
+
+#define FOO pow
+#include "mpcheck-template3.c"
+
+#define FOO abs
+#include "mpcheck-template2.c"
+
+#define FOO arg
+#include "mpcheck-template2.c"
+
+#define FOO sqrt
+#include "mpcheck-template1.c"
+
+#define FOO acos
+#include "mpcheck-template1.c"
+
+#define FOO acosh
+#include "mpcheck-template1.c"
+
+#define FOO asin
+#include "mpcheck-template1.c"
+
+#define FOO asinh
+#include "mpcheck-template1.c"
+
+#define FOO atan
+#include "mpcheck-template1.c"
+
+#define FOO atanh
+#include "mpcheck-template1.c"
+
+#define FOO cos
+#include "mpcheck-template1.c"
+
+#define FOO cosh
+#include "mpcheck-template1.c"
+
+#define FOO exp
+#include "mpcheck-template1.c"
+
+#define FOO log
+#include "mpcheck-template1.c"
+
+#define FOO log10
+#include "mpcheck-template1.c"
+
+#define FOO sin
+#include "mpcheck-template1.c"
+
+#define FOO sinh
+#include "mpcheck-template1.c"
+
+#define FOO tan
+#include "mpcheck-template1.c"
+
+#define FOO tanh
+#include "mpcheck-template1.c"
+
+int
+main (int argc, char *argv[])
+{
+ mpfr_prec_t p = PRECISION; /* precision of 'float' */
+ unsigned long n = 1000000; /* default number of random tests per function */
+
+ while (argc >= 2 && argv[1][0] == '-')
+ {
+ if (argc >= 3 && strcmp (argv[1], "-p") == 0)
+ {
+ p = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-seed") == 0)
+ {
+ seed = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-num") == 0)
+ {
+ n = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (strcmp (argv[1], "-v") == 0)
+ {
+ verbose ++;
+ argc --;
+ argv ++;
+ }
+ else if (strcmp (argv[1], "-check") == 0)
+ {
+ recheck = 1;
+ argc --;
+ argv ++;
+ }
+ else
+ {
+ fprintf (stderr, "Unknown option %s\n", argv[1]);
+ exit (1);
+ }
+ }
+
+ /* set exponent range */
+ emin = -EMAX - PRECISION + 4; /* should be -148 */
+ emax = EMAX;
+ mpfr_set_emin (emin);
+ mpfr_set_emax (emax);
+
+ gmp_randinit_default (state);
+ mpz_init (expz);
+
+#ifdef __GNUC__
+ printf ("GNU libc version: %s\n", gnu_get_libc_version ());
+ printf ("GNU libc release: %s\n", gnu_get_libc_release ());
+#endif
+
+ if (seed == 0)
+ seed = getpid ();
+ printf ("Using random seed %lu\n", seed);
+
+ /* (complex,complex) -> complex */
+ test_add (p, n);
+ test_sub (p, n);
+ test_mul (p, n);
+ test_div (p, n);
+ test_pow (p, n);
+
+ /* complex -> real */
+ test_abs (p, n);
+ test_arg (p, n);
+
+ /* complex -> complex */
+ test_sqrt (p, n);
+ test_acos (p, n);
+ test_acosh (p, n);
+ test_asin (p, n);
+ test_asinh (p, n);
+ test_atan (p, n);
+ test_atanh (p, n);
+ test_cos (p, n);
+ test_cosh (p, n);
+ test_exp (p, n);
+ test_log (p, n);
+ test_log10 (p, n);
+ test_sin (p, n);
+ test_sinh (p, n);
+ test_tan (p, n);
+ test_tanh (p, n);
+
+ gmp_randclear (state);
+ mpz_clear (expz);
+
+ report_maximal_errors ();
+
+ return 0;
+}
diff --git a/tools/mpcheck/mpcheck-float128.c b/tools/mpcheck/mpcheck-float128.c
new file mode 100644
index 0000000..ba08efd
--- /dev/null
+++ b/tools/mpcheck/mpcheck-float128.c
@@ -0,0 +1,252 @@
+/* mpcheck-float128 -- compare mpc functions against "__float128 complex"
+ from the GNU libc implementation
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+/* the GNU libc provides the following functions (as of 2.31),
+ with 'f' suffix for the float/binary32 version, with no suffix
+ for the double/binary64 version, with 'l' suffix for the long double
+ version, and with 'f128' suffix for the __float128 version:
+
+ cabs casinh cexp csinh
+ cacos catan clog csqrt
+ cacosh catanh clog10 ctan
+ carg ccos cpow ctanh
+ casin ccosh csin
+*/
+
+#define _GNU_SOURCE /* for clog10 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <complex.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#define MPFR_WANT_FLOAT128
+#include "mpc.h"
+#ifdef __GNUC__
+#include <gnu/libc-version.h>
+#endif
+
+#define PRECISION 113
+#define EMAX 16384
+#define TYPE _Float128
+#define SUFFIX f128
+
+#define mpfr_set_type mpfr_set_float128
+
+static TYPE complex
+mpc_get_type (mpc_t z, mpc_rnd_t rnd)
+{
+ TYPE x, y;
+ /* there is no mpc_get_float128c function */
+ x = mpfr_get_float128 (mpc_realref (z), MPC_RND_RE(rnd));
+ y = mpfr_get_float128 (mpc_imagref (z), MPC_RND_IM(rnd));
+ return x + I * y;
+}
+
+static int
+mpc_set_type (mpc_t x, TYPE complex y, mpc_rnd_t rnd)
+{
+ /* there is no mpc_set_float128c function */
+ mpfr_set_float128 (mpc_realref (x), crealf128 (y), MPC_RND_RE(rnd));
+ mpfr_set_float128 (mpc_imagref (x), cimagf128 (y), MPC_RND_IM(rnd));
+}
+
+gmp_randstate_t state;
+mpz_t expz; /* global variable used in mpcheck_random */
+unsigned long seed = 0;
+int verbose = 0;
+mpfr_exp_t emin, emax;
+
+#include "mpcheck-common.c"
+
+#define FOO add
+#define CFOO(x,y) (x+y)
+#include "mpcheck-template3.c"
+
+#define FOO sub
+#define CFOO(x,y) (x-y)
+#include "mpcheck-template3.c"
+
+#define FOO mul
+#define CFOO(x,y) (x*y)
+#include "mpcheck-template3.c"
+
+#define FOO div
+#define CFOO(x,y) (x/y)
+#include "mpcheck-template3.c"
+
+#define FOO pow
+#include "mpcheck-template3.c"
+
+#define FOO abs
+#include "mpcheck-template2.c"
+
+#define FOO arg
+#include "mpcheck-template2.c"
+
+#define FOO sqrt
+#include "mpcheck-template1.c"
+
+#define FOO acos
+#include "mpcheck-template1.c"
+
+#define FOO acosh
+#include "mpcheck-template1.c"
+
+#define FOO asin
+#include "mpcheck-template1.c"
+
+#define FOO asinh
+#include "mpcheck-template1.c"
+
+#define FOO atan
+#include "mpcheck-template1.c"
+
+#define FOO atanh
+#include "mpcheck-template1.c"
+
+#define FOO cos
+#include "mpcheck-template1.c"
+
+#define FOO cosh
+#include "mpcheck-template1.c"
+
+#define FOO exp
+#include "mpcheck-template1.c"
+
+#define FOO log
+#include "mpcheck-template1.c"
+
+#define FOO log10
+#include "mpcheck-template1.c"
+
+#define FOO sin
+#include "mpcheck-template1.c"
+
+#define FOO sinh
+#include "mpcheck-template1.c"
+
+#define FOO tan
+#include "mpcheck-template1.c"
+
+#define FOO tanh
+#include "mpcheck-template1.c"
+
+int
+main (int argc, char *argv[])
+{
+ mpfr_prec_t p = PRECISION; /* precision of 'double' */
+ unsigned long n = 1000000; /* default number of random tests per function */
+
+ while (argc >= 2 && argv[1][0] == '-')
+ {
+ if (argc >= 3 && strcmp (argv[1], "-p") == 0)
+ {
+ p = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-seed") == 0)
+ {
+ seed = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-num") == 0)
+ {
+ n = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (strcmp (argv[1], "-v") == 0)
+ {
+ verbose ++;
+ argc --;
+ argv ++;
+ }
+ else if (strcmp (argv[1], "-check") == 0)
+ {
+ recheck = 1;
+ argc --;
+ argv ++;
+ }
+ else
+ {
+ fprintf (stderr, "Unknown option %s\n", argv[1]);
+ exit (1);
+ }
+ }
+
+ /* set exponent range */
+ emin = -EMAX - 64 + 4; /* should be -16444 like for long double */
+ emax = EMAX;
+ mpfr_set_emin (emin);
+ mpfr_set_emax (emax);
+
+ gmp_randinit_default (state);
+ mpz_init (expz);
+
+#ifdef __GNUC__
+ printf ("GNU libc version: %s\n", gnu_get_libc_version ());
+ printf ("GNU libc release: %s\n", gnu_get_libc_release ());
+#endif
+
+ if (seed == 0)
+ seed = getpid ();
+ printf ("Using random seed %lu\n", seed);
+
+ /* (complex,complex) -> complex */
+ test_add (p, n);
+ test_sub (p, n);
+ test_mul (p, n);
+ test_div (p, n);
+ test_pow (p, n);
+
+ /* complex -> real */
+ test_abs (p, n);
+ test_arg (p, n);
+
+ /* complex -> complex */
+ test_sqrt (p, n);
+ test_acos (p, n);
+ test_acosh (p, n);
+ test_asin (p, n);
+ test_asinh (p, n);
+ test_atan (p, n);
+ test_atanh (p, n);
+ test_cos (p, n);
+ test_cosh (p, n);
+ test_exp (p, n);
+ test_log (p, n);
+ test_log10 (p, n);
+ test_sin (p, n);
+ test_sinh (p, n);
+ test_tan (p, n);
+ test_tanh (p, n);
+
+ gmp_randclear (state);
+ mpz_clear (expz);
+
+ report_maximal_errors ();
+
+ return 0;
+}
diff --git a/tools/mpcheck/mpcheck-longdouble.c b/tools/mpcheck/mpcheck-longdouble.c
new file mode 100644
index 0000000..6f7aefc
--- /dev/null
+++ b/tools/mpcheck/mpcheck-longdouble.c
@@ -0,0 +1,235 @@
+/* mpcheck-longdouble -- compare mpc functions against "long double complex"
+ from the GNU libc implementation
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+/* the GNU libc provides the following functions (as of 2.31),
+ with 'f' suffix for the float/binary32 version, with no suffix
+ for the double/binary64 version, with 'l' suffix for the long double
+ version, and with 'f128' suffix for the __float128 version:
+
+ cabs casinh cexp csinh
+ cacos catan clog csqrt
+ cacosh catanh clog10 ctan
+ carg ccos cpow ctanh
+ casin ccosh csin
+*/
+
+#define _GNU_SOURCE /* for clog10 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <complex.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include "mpc.h"
+#ifdef __GNUC__
+#include <gnu/libc-version.h>
+#endif
+
+#define PRECISION 64
+#define EMAX 16384
+#define TYPE long double
+#define SUFFIX l
+
+#define mpc_get_type mpc_get_ldc
+#define mpc_set_type mpc_set_ldc
+#define mpfr_set_type mpfr_set_ld
+
+gmp_randstate_t state;
+mpz_t expz; /* global variable used in mpcheck_random */
+unsigned long seed = 0;
+int verbose = 0;
+mpfr_exp_t emin, emax;
+
+#include "mpcheck-common.c"
+
+#define FOO add
+#define CFOO(x,y) (x+y)
+#include "mpcheck-template3.c"
+
+#define FOO sub
+#define CFOO(x,y) (x-y)
+#include "mpcheck-template3.c"
+
+#define FOO mul
+#define CFOO(x,y) (x*y)
+#include "mpcheck-template3.c"
+
+#define FOO div
+#define CFOO(x,y) (x/y)
+#include "mpcheck-template3.c"
+
+#define FOO pow
+#include "mpcheck-template3.c"
+
+#define FOO abs
+#include "mpcheck-template2.c"
+
+#define FOO arg
+#include "mpcheck-template2.c"
+
+#define FOO sqrt
+#include "mpcheck-template1.c"
+
+#define FOO acos
+#include "mpcheck-template1.c"
+
+#define FOO acosh
+#include "mpcheck-template1.c"
+
+#define FOO asin
+#include "mpcheck-template1.c"
+
+#define FOO asinh
+#include "mpcheck-template1.c"
+
+#define FOO atan
+#include "mpcheck-template1.c"
+
+#define FOO atanh
+#include "mpcheck-template1.c"
+
+#define FOO cos
+#include "mpcheck-template1.c"
+
+#define FOO cosh
+#include "mpcheck-template1.c"
+
+#define FOO exp
+#include "mpcheck-template1.c"
+
+#define FOO log
+#include "mpcheck-template1.c"
+
+#define FOO log10
+#include "mpcheck-template1.c"
+
+#define FOO sin
+#include "mpcheck-template1.c"
+
+#define FOO sinh
+#include "mpcheck-template1.c"
+
+#define FOO tan
+#include "mpcheck-template1.c"
+
+#define FOO tanh
+#include "mpcheck-template1.c"
+
+int
+main (int argc, char *argv[])
+{
+ mpfr_prec_t p = PRECISION; /* precision of 'double' */
+ unsigned long n = 1000000; /* default number of random tests per function */
+
+ while (argc >= 2 && argv[1][0] == '-')
+ {
+ if (argc >= 3 && strcmp (argv[1], "-p") == 0)
+ {
+ p = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-seed") == 0)
+ {
+ seed = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc >= 3 && strcmp (argv[1], "-num") == 0)
+ {
+ n = atoi (argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (strcmp (argv[1], "-v") == 0)
+ {
+ verbose ++;
+ argc --;
+ argv ++;
+ }
+ else if (strcmp (argv[1], "-check") == 0)
+ {
+ recheck = 1;
+ argc --;
+ argv ++;
+ }
+ else
+ {
+ fprintf (stderr, "Unknown option %s\n", argv[1]);
+ exit (1);
+ }
+ }
+
+ /* set exponent range */
+ emin = -EMAX - PRECISION + 4; /* should be -16444 */
+ emax = EMAX;
+ mpfr_set_emin (emin);
+ mpfr_set_emax (emax);
+
+ gmp_randinit_default (state);
+ mpz_init (expz);
+
+#ifdef __GNUC__
+ printf ("GNU libc version: %s\n", gnu_get_libc_version ());
+ printf ("GNU libc release: %s\n", gnu_get_libc_release ());
+#endif
+
+ if (seed == 0)
+ seed = getpid ();
+ printf ("Using random seed %lu\n", seed);
+
+ /* (complex,complex) -> complex */
+ test_add (p, n);
+ test_sub (p, n);
+ test_mul (p, n);
+ test_div (p, n);
+ test_pow (p, n);
+
+ /* complex -> real */
+ test_abs (p, n);
+ test_arg (p, n);
+
+ /* complex -> complex */
+ test_sqrt (p, n);
+ test_acos (p, n);
+ test_acosh (p, n);
+ test_asin (p, n);
+ test_asinh (p, n);
+ test_atan (p, n);
+ test_atanh (p, n);
+ test_cos (p, n);
+ test_cosh (p, n);
+ test_exp (p, n);
+ test_log (p, n);
+ test_log10 (p, n);
+ test_sin (p, n);
+ test_sinh (p, n);
+ test_tan (p, n);
+ test_tanh (p, n);
+
+ gmp_randclear (state);
+ mpz_clear (expz);
+
+ report_maximal_errors ();
+
+ return 0;
+}
diff --git a/tools/mpcheck/mpcheck-template1.c b/tools/mpcheck/mpcheck-template1.c
new file mode 100644
index 0000000..ee56e01
--- /dev/null
+++ b/tools/mpcheck/mpcheck-template1.c
@@ -0,0 +1,155 @@
+/* template for complex -> complex function, for example acos
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+#define CAT1(X,Y) X ## Y
+#define CAT2(X,Y) CAT1(X,Y)
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+#define FUN CAT2(test_,FOO)
+#define CHECK CAT2(check_,FOO)
+#define MPC_FOO CAT2(mpc_,FOO)
+#define CFOO CAT2(CAT2(c,FOO),SUFFIX)
+#define BAR TOSTRING(FOO)
+
+#define EXTRA 20
+
+/* routine to double-check result from MPC with more precision and without
+ reduced exponent range */
+static void
+CHECK (mpc_t x, mpc_t z)
+{
+ mpfr_exp_t saved_emin = mpfr_get_emin ();
+ mpfr_exp_t saved_emax = mpfr_get_emax ();
+ mpfr_prec_t p = mpfr_get_prec (mpc_realref (x)), pp = p + EXTRA;
+ mpc_t t;
+ int ok, inex_re, inex_im;
+
+ /* enlarge exponent range to the largest possible */
+ mpfr_set_emin (mpfr_get_emin_min ());
+ mpfr_set_emax (mpfr_get_emax_max ());
+
+ mpc_init2 (t, pp);
+ MPC_FOO (t, x, MPC_RNDNN);
+ inex_re = mpfr_prec_round (mpc_realref (t), p, MPFR_RNDN);
+ inex_im = mpfr_prec_round (mpc_imagref (t), p, MPFR_RNDN);
+ /* restore exponent range */
+ mpfr_set_emin (saved_emin);
+ mpfr_set_emax (saved_emax);
+ inex_re = mpfr_check_range (mpc_realref (t), inex_re, MPFR_RNDN);
+ inex_re = mpfr_subnormalize (mpc_realref (t), inex_re, MPFR_RNDN);
+ inex_im = mpfr_check_range (mpc_imagref (t), inex_im, MPFR_RNDN);
+ inex_im = mpfr_subnormalize (mpc_imagref (t), inex_im, MPFR_RNDN);
+
+ /* check real parts agree */
+ ok = mpfr_agree (mpc_realref (z), mpc_realref (t), inex_re)
+ && mpfr_agree (mpc_imagref (z), mpc_imagref (t), inex_im);
+
+ if (!ok)
+ {
+ mpfr_printf ("Potential bug in mpc_%s for x=(%Re,%Re)\n", BAR,
+ mpc_realref (x), mpc_imagref (x));
+ mpfr_printf (" mpc_%s to precision %lu gives (%Re,%Re)\n", BAR, p,
+ mpc_realref (z), mpc_imagref (z));
+ mpfr_printf (" mpc_%s to precision %lu gives (%Re,%Re)\n", BAR, pp,
+ mpc_realref (t), mpc_imagref (t));
+ exit (1);
+ }
+
+ mpc_clear (t);
+}
+
+static void
+FUN (mpfr_prec_t p, unsigned long n)
+{
+ unsigned long i = 0;
+ mpc_t x, z, t;
+ TYPE complex xx, zz;
+ int inex, cmp;
+ unsigned long errors = 0, max_err_re = 0, max_err_im = 0;
+ /* allow reduced exponent range */
+#if defined(FOO_EMIN) || defined(FOO_EMAX)
+ mpfr_exp_t saved_emin = emin, saved_emax = emax;
+ emin = FOO_EMIN;
+ emax = FOO_EMAX;
+#endif
+
+ if (verbose > 0)
+ printf ("Testing function %s\n", BAR);
+
+ gmp_randseed_ui (state, seed);
+
+ mpc_init2 (x, p);
+ mpc_init2 (z, p);
+ mpc_init2 (t, p);
+ for (i = 0; i < n; i++)
+ {
+ mpcheck_random (x);
+ inex = MPC_FOO (z, x, MPC_RNDNN);
+ mpfr_subnormalize (mpc_realref (z), MPC_INEX_RE(inex), MPFR_RNDN);
+ mpfr_subnormalize (mpc_imagref (z), MPC_INEX_IM(inex), MPFR_RNDN);
+ xx = mpc_get_type (x, MPC_RNDNN);
+ zz = CFOO (xx);
+ mpc_set_type (t, zz, MPFR_RNDN);
+ cmp = mpcheck_mpc_cmp (t, z);
+ if (cmp != 0)
+ {
+ unsigned long err_re = ulp_error (mpc_realref (t), mpc_realref (z));
+ unsigned long err_im = ulp_error (mpc_imagref (t), mpc_imagref (z));
+ if (verbose > 0 && (err_re > max_err_re || err_im > max_err_im))
+ {
+ mpfr_printf (" mpc_%s and c%s differ by (%lu,%lu) ulp(s)\n for x=(%Re,%Re)\n",
+ BAR, BAR, err_re, err_im,
+ mpc_realref (x), mpc_imagref (x));
+ mpfr_printf (" mpc_%s gives (%Re,%Re)\n", BAR,
+ mpc_realref (z), mpc_imagref (z));
+ mpfr_printf (" c%s gives (%Re,%Re)\n", BAR,
+ mpc_realref (t), mpc_imagref (t));
+ }
+ if (recheck)
+ CHECK (x, z);
+ errors ++;
+ if (err_re > max_err_re)
+ {
+ max_err_re = err_re;
+ if (max_err_re > Max_err_re)
+ Max_err_re = max_err_re;
+ }
+ if (err_im > max_err_im)
+ {
+ max_err_im = err_im;
+ if (max_err_im > Max_err_im)
+ Max_err_im = max_err_im;
+ }
+ }
+ }
+ mpc_clear (x);
+ mpc_clear (z);
+ mpc_clear (t);
+ printf ("Errors for %s: %lu/%lu (max %lu,%lu) [seed %lu]\n", BAR,
+ errors, n, max_err_re, max_err_im, seed);
+#if defined(FOO_EMIN) || defined(FOO_EMAX)
+ emin = saved_emin;
+ emax = saved_emax;
+#endif
+}
+
+#undef FOO
+#undef CFOO
diff --git a/tools/mpcheck/mpcheck-template2.c b/tools/mpcheck/mpcheck-template2.c
new file mode 100644
index 0000000..7a2a8c0
--- /dev/null
+++ b/tools/mpcheck/mpcheck-template2.c
@@ -0,0 +1,130 @@
+/* template for complex -> real function, for example abs
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+#define CAT1(X,Y) X ## Y
+#define CAT2(X,Y) CAT1(X,Y)
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+#define FUN CAT2(test_,FOO)
+#define CHECK CAT2(check_,FOO)
+#define MPC_FOO CAT2(mpc_,FOO)
+#define CFOO CAT2(CAT2(c,FOO),SUFFIX)
+#define BAR TOSTRING(FOO)
+
+#define EXTRA 20
+
+/* routine to double-check result from MPC with more precision and without
+ reduced exponent range */
+static void
+CHECK (mpc_t x, mpfr_t z)
+{
+ mpfr_exp_t saved_emin = mpfr_get_emin ();
+ mpfr_exp_t saved_emax = mpfr_get_emax ();
+ mpfr_prec_t p = mpfr_get_prec (mpc_realref (x)), pp = p + EXTRA;
+ mpfr_t t;
+ int inex, ok;
+
+ /* enlarge exponent range to the largest possible */
+ mpfr_set_emin (mpfr_get_emin_min ());
+ mpfr_set_emax (mpfr_get_emax_max ());
+
+ mpfr_init2 (t, pp);
+ MPC_FOO (t, x, MPFR_RNDN);
+ inex = mpfr_prec_round (t, p, MPFR_RNDN);
+ /* restore exponent range */
+ mpfr_set_emin (saved_emin);
+ mpfr_set_emax (saved_emax);
+ inex = mpfr_check_range (t, inex, MPFR_RNDN);
+ inex = mpfr_subnormalize (t, inex, MPFR_RNDN);
+
+ /* check real parts agree */
+ ok = mpfr_agree (z, t, inex);
+
+ if (!ok)
+ {
+ mpfr_printf ("Potential bug in mpc_%s for x=(%Re,%Re)\n", BAR,
+ mpc_realref (x), mpc_imagref (x));
+ mpfr_printf (" mpc_%s to precision %lu gives %Re\n", BAR, p, z);
+ mpfr_printf (" mpc_%s to precision %lu gives %Re\n", BAR, pp, t);
+ exit (1);
+ }
+
+ mpfr_clear (t);
+}
+
+static void
+FUN (mpfr_prec_t p, unsigned long n)
+{
+ unsigned long i = 0;
+ mpc_t x;
+ mpfr_t z, t;
+ TYPE complex xx;
+ TYPE zz;
+ int inex;
+ unsigned long errors = 0, max_err = 0;
+
+ if (verbose > 0)
+ printf ("Testing function %s\n", BAR);
+
+ gmp_randseed_ui (state, seed);
+
+ mpc_init2 (x, p);
+ mpfr_init2 (z, p);
+ mpfr_init2 (t, p);
+ for (i = 0; i < n; i++)
+ {
+ mpcheck_random (x);
+ inex = MPC_FOO (z, x, MPC_RNDNN);
+ mpfr_subnormalize (z, inex, MPFR_RNDN);
+ xx = mpc_get_type (x, MPC_RNDNN);
+ zz = CFOO (xx);
+ mpfr_set_type (t, zz, MPFR_RNDN);
+ if (mpcheck_mpfr_cmp (t, z) != 0)
+ {
+ unsigned long err = ulp_error (t, z);
+ if (verbose > 0 && err > max_err)
+ {
+ mpfr_printf (" mpc_%s and c%s differ by %lu ulp(s)\n for x=(%Re,%Re)\n",
+ BAR, BAR, err,
+ mpc_realref (x), mpc_imagref (x));
+ mpfr_printf (" mpc_%s gives %Re\n", BAR, z);
+ mpfr_printf (" c%s gives %Re\n", BAR, t);
+ }
+ if (recheck)
+ CHECK (x, z);
+ errors++;
+ if (err > max_err)
+ {
+ max_err = err;
+ if (max_err > Max_err)
+ Max_err = max_err;
+ }
+ }
+ }
+ mpc_clear (x);
+ mpfr_clear (z);
+ mpfr_clear (t);
+ printf ("Errors for %s: %lu/%lu (max %lu) [seed %lu]\n",
+ BAR, errors, n, max_err, seed);
+}
+
+#undef FOO
+#undef CFOO
diff --git a/tools/mpcheck/mpcheck-template3.c b/tools/mpcheck/mpcheck-template3.c
new file mode 100644
index 0000000..e6718a8
--- /dev/null
+++ b/tools/mpcheck/mpcheck-template3.c
@@ -0,0 +1,172 @@
+/* template for (complex,complex) -> complex function, for example add
+
+Copyright (C) 2020 INRIA
+
+This file is part of GNU MPC.
+
+GNU MPC is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+GNU MPC 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 Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see http://www.gnu.org/licenses/ .
+*/
+
+#define CAT1(X,Y) X ## Y
+#define CAT2(X,Y) CAT1(X,Y)
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+#define FUN CAT2(test_,FOO)
+#define CHECK CAT2(check_,FOO)
+#define MPC_FOO CAT2(mpc_,FOO)
+#ifndef CFOO
+#define CFOO CAT2(CAT2(c,FOO),SUFFIX)
+#endif
+#define BAR TOSTRING(FOO)
+
+#define EXTRA 20
+
+/* routine to double-check result from MPC with more precision and without
+ reduced exponent range */
+static void
+CHECK (mpc_t x, mpc_t y, mpc_t z)
+{
+ mpfr_exp_t saved_emin = mpfr_get_emin ();
+ mpfr_exp_t saved_emax = mpfr_get_emax ();
+ mpfr_prec_t p = mpfr_get_prec (mpc_realref (x)), pp = p + EXTRA;
+ mpc_t t;
+ int ok, inex_re, inex_im;
+
+ /* enlarge exponent range to the largest possible */
+ mpfr_set_emin (mpfr_get_emin_min ());
+ mpfr_set_emax (mpfr_get_emax_max ());
+
+ mpc_init2 (t, pp);
+ MPC_FOO (t, x, y, MPC_RNDNN);
+ inex_re = mpfr_prec_round (mpc_realref (t), p, MPFR_RNDN);
+ inex_im = mpfr_prec_round (mpc_imagref (t), p, MPFR_RNDN);
+ /* restore exponent range */
+ mpfr_set_emin (saved_emin);
+ mpfr_set_emax (saved_emax);
+ inex_re = mpfr_check_range (mpc_realref (t), inex_re, MPFR_RNDN);
+ inex_re = mpfr_subnormalize (mpc_realref (t), inex_re, MPFR_RNDN);
+ inex_im = mpfr_check_range (mpc_imagref (t), inex_im, MPFR_RNDN);
+ inex_im = mpfr_subnormalize (mpc_imagref (t), inex_im, MPFR_RNDN);
+
+ /* check real parts agree */
+ ok = mpfr_agree (mpc_realref (z), mpc_realref (t), inex_re)
+ && mpfr_agree (mpc_imagref (z), mpc_imagref (t), inex_im);
+
+ if (!ok)
+ {
+ mpfr_printf ("Potential bug in mpc_%s for\n", BAR);
+ mpfr_printf (" x=(%Re,%Re)\n", mpc_realref (x), mpc_imagref (x));
+ mpfr_printf (" y=(%Re,%Re)\n", mpc_realref (y), mpc_imagref (y));
+ mpfr_printf (" mpc_%s to precision %lu gives (%Re,%Re)\n", BAR, p,
+ mpc_realref (z), mpc_imagref (z));
+ mpfr_printf (" mpc_%s to precision %lu gives (%Re,%Re)\n", BAR, pp,
+ mpc_realref (t), mpc_imagref (t));
+ exit (1);
+ }
+
+ mpc_clear (t);
+}
+
+static void
+FUN (mpfr_prec_t p, unsigned long n)
+{
+ unsigned long i = 0;
+ mpc_t x, y, z, t;
+ TYPE complex xx, yy, zz;
+ int inex;
+ unsigned long errors = 0, max_err_re = 0, max_err_im = 0;
+ unsigned long special_errors = 0;
+ int cmp;
+
+ if (verbose > 0)
+ printf ("Testing function %s\n", BAR);
+
+ gmp_randseed_ui (state, seed);
+
+ mpc_init2 (x, p);
+ mpc_init2 (y, p);
+ mpc_init2 (z, p);
+ mpc_init2 (t, p);
+ for (i = 0; i < n; i++)
+ {
+ mpcheck_random (x);
+ mpcheck_random (y);
+ inex = MPC_FOO (z, x, y, MPC_RNDNN);
+ mpfr_subnormalize (mpc_realref (z), MPC_INEX_RE(inex), MPFR_RNDN);
+ mpfr_subnormalize (mpc_imagref (z), MPC_INEX_IM(inex), MPFR_RNDN);
+ xx = mpc_get_type (x, MPC_RNDNN);
+ yy = mpc_get_dc (y, MPC_RNDNN);
+ zz = CFOO(xx, yy);
+ mpc_set_type (t, zz, MPFR_RNDN);
+ cmp = mpcheck_mpc_cmp (t, z);
+ if (cmp > 1) /* NaN or Inf */
+ {
+ if (verbose > 1 || (verbose == 1 && special_errors == 0))
+ {
+ printf (" mpc_%s and c%s differ\n", BAR, BAR);
+ mpfr_printf (" for x=(%Re,%Re)\n y=(%Re,%Re)\n",
+ mpc_realref (x), mpc_imagref (x),
+ mpc_realref (y), mpc_imagref (y));
+ mpfr_printf (" mpc_%s gives (%Re,%Re)\n", BAR,
+ mpc_realref (z), mpc_imagref (z));
+ mpfr_printf (" c%s gives (%Re,%Re)\n", BAR,
+ mpc_realref (t), mpc_imagref (t));
+ special_errors ++;
+ }
+ errors ++;
+ }
+ else if (cmp != 0)
+ {
+ unsigned long err_re = ulp_error (mpc_realref (t), mpc_realref (z));
+ unsigned long err_im = ulp_error (mpc_imagref (t), mpc_imagref (z));
+ if (verbose > 0 && (err_re > max_err_re || err_im > max_err_im))
+ {
+ printf (" mpc_%s and c%s differ by (%lu,%lu) ulp(s)\n",
+ BAR, BAR, err_re, err_im);
+ mpfr_printf (" for x=(%Re,%Re)\n y=(%Re,%Re)\n",
+ mpc_realref (x), mpc_imagref (x),
+ mpc_realref (y), mpc_imagref (y));
+ mpfr_printf (" mpc_%s gives (%Re,%Re)\n", BAR,
+ mpc_realref (z), mpc_imagref (z));
+ mpfr_printf (" c%s gives (%Re,%Re)\n", BAR,
+ mpc_realref (t), mpc_imagref (t));
+ }
+ errors ++;
+ if (err_re > max_err_re)
+ {
+ max_err_re = err_re;
+ if (max_err_re > Max_err_re)
+ Max_err_re = max_err_re;
+ }
+ if (err_im > max_err_im)
+ {
+ max_err_im = err_im;
+ if (max_err_im > Max_err_im)
+ Max_err_im = max_err_im;
+ }
+ }
+ if (recheck && cmp != 0)
+ CHECK (x, y, z);
+ }
+ mpc_clear (x);
+ mpc_clear (y);
+ mpc_clear (z);
+ mpc_clear (t);
+ printf ("Errors for %s: %lu/%lu (max %lu,%lu) [seed %lu]\n", BAR, errors, n,
+ max_err_re, max_err_im, seed);
+}
+
+#undef FOO
+#undef CFOO