mirror of
https://review.haiku-os.org/buildtools
synced 2025-02-07 22:44:46 +01:00
959 lines
26 KiB
C
959 lines
26 KiB
C
/* Miscellaneous support for test programs.
|
|
|
|
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
|
|
Contributed by the AriC and Caramel projects, INRIA.
|
|
|
|
This file is part of the GNU MPFR Library.
|
|
|
|
The GNU MPFR Library 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.
|
|
|
|
The GNU MPFR 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 Lesser General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see
|
|
http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
|
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# if HAVE_CONFIG_H
|
|
# include "config.h" /* for a build within gmp */
|
|
# endif
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <float.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef HAVE_LOCALE_H
|
|
#include <locale.h>
|
|
#endif
|
|
|
|
#ifdef MPFR_TEST_DIVBYZERO
|
|
# include <fenv.h>
|
|
#endif
|
|
|
|
#ifdef TIME_WITH_SYS_TIME
|
|
# include <sys/time.h> /* for struct timeval */
|
|
# include <time.h>
|
|
#elif defined HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
#else
|
|
# include <time.h>
|
|
#endif
|
|
|
|
/* <sys/fpu.h> is needed to have union fpc_csr defined under IRIX64
|
|
(see below). Let's include it only if need be. */
|
|
#if defined HAVE_SYS_FPU_H && defined HAVE_FPC_CSR
|
|
# include <sys/fpu.h>
|
|
#endif
|
|
|
|
#ifdef MPFR_TESTS_TIMEOUT
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
#include "mpfr-test.h"
|
|
|
|
#ifdef MPFR_FPU_PREC
|
|
/* This option allows to test MPFR on x86 processors when the FPU
|
|
* rounding precision has been changed. As MPFR is a library, this can
|
|
* occur in practice, either by the calling software or by some other
|
|
* library or plug-in used by the calling software. This option is
|
|
* mainly for developers. If it is used, then the <fpu_control.h>
|
|
* header is assumed to exist and work like under Linux/x86. MPFR does
|
|
* not need to be recompiled. So, a possible usage is the following:
|
|
*
|
|
* cd tests
|
|
* make clean
|
|
* make check CFLAGS="-g -O2 -ffloat-store -DMPFR_FPU_PREC=_FPU_SINGLE"
|
|
*
|
|
* i.e. just add -DMPFR_FPU_PREC=... to the CFLAGS found in Makefile.
|
|
*
|
|
* Notes:
|
|
* + SSE2 (used to implement double's on x86_64, and possibly on x86
|
|
* too, depending on the compiler configuration and flags) is not
|
|
* affected by the dynamic precision.
|
|
* + When the FPU is set to single precision, the behavior of MPFR
|
|
* functions that have a native floating-point type (float, double,
|
|
* long double) as argument or return value is not guaranteed.
|
|
*/
|
|
|
|
#include <fpu_control.h>
|
|
|
|
static void
|
|
set_fpu_prec (void)
|
|
{
|
|
fpu_control_t cw;
|
|
|
|
_FPU_GETCW(cw);
|
|
cw &= ~(_FPU_EXTENDED|_FPU_DOUBLE|_FPU_SINGLE);
|
|
cw |= (MPFR_FPU_PREC);
|
|
_FPU_SETCW(cw);
|
|
}
|
|
|
|
#endif
|
|
|
|
static mpfr_exp_t default_emin, default_emax;
|
|
|
|
static void tests_rand_start (void);
|
|
static void tests_rand_end (void);
|
|
static void tests_limit_start (void);
|
|
|
|
/* We want to always import the function mpfr_dump inside the test
|
|
suite, so that we can use it in GDB. But it doesn't work if we build
|
|
a Windows DLL (initializer element is not a constant) */
|
|
#if !__GMP_LIBGMP_DLL
|
|
extern void (*dummy_func) (mpfr_srcptr);
|
|
void (*dummy_func)(mpfr_srcptr) = mpfr_dump;
|
|
#endif
|
|
|
|
void
|
|
test_version (void)
|
|
{
|
|
const char *version;
|
|
|
|
/* VL: I get the following error on an OpenSUSE machine, and changing
|
|
the value of shlibpath_overrides_runpath in the libtool file from
|
|
'no' to 'yes' fixes the problem. */
|
|
|
|
version = mpfr_get_version ();
|
|
if (strcmp (MPFR_VERSION_STRING, version) == 0)
|
|
{
|
|
char buffer[16];
|
|
int i;
|
|
|
|
sprintf (buffer, "%d.%d.%d", MPFR_VERSION_MAJOR, MPFR_VERSION_MINOR,
|
|
MPFR_VERSION_PATCHLEVEL);
|
|
for (i = 0; buffer[i] == version[i]; i++)
|
|
if (buffer[i] == '\0')
|
|
return;
|
|
if (buffer[i] == '\0' && version[i] == '-')
|
|
return;
|
|
printf ("MPFR_VERSION_MAJOR.MPFR_VERSION_MINOR.MPFR_VERSION_PATCHLEVEL"
|
|
" (%s)\nand MPFR_VERSION_STRING (%s) do not match!\nIt seems "
|
|
"that the mpfr.h file has been corrupted.\n", buffer, version);
|
|
exit (1);
|
|
}
|
|
|
|
printf ("Incorrect MPFR version! (%s header vs %s library)\n"
|
|
"Nothing else has been tested since for this reason,\n"
|
|
"any other test may fail. Please fix this one first.\n\n"
|
|
"You can try to avoid this problem by changing the value of\n"
|
|
"shlibpath_overrides_runpath in the libtool file and rebuild\n"
|
|
"MPFR (make clean && make && make check).\n"
|
|
"Otherwise this error may be due to a corrupted mpfr.h, an\n"
|
|
"incomplete build (try to rebuild MPFR from scratch and/or\n"
|
|
"use 'make clean'), or something wrong in the system.\n",
|
|
MPFR_VERSION_STRING, version);
|
|
exit (1);
|
|
}
|
|
|
|
void
|
|
tests_start_mpfr (void)
|
|
{
|
|
test_version ();
|
|
|
|
/* don't buffer, so output is not lost if a test causes a segv etc */
|
|
setbuf (stdout, NULL);
|
|
|
|
#if defined HAVE_LOCALE_H && defined HAVE_SETLOCALE
|
|
/* Added on 2005-07-09. This allows to test MPFR under various
|
|
locales. New bugs will probably be found, in particular with
|
|
LC_ALL="tr_TR.ISO8859-9" because of the i/I character... */
|
|
setlocale (LC_ALL, "");
|
|
#endif
|
|
|
|
#ifdef MPFR_FPU_PREC
|
|
set_fpu_prec ();
|
|
#endif
|
|
|
|
#ifdef MPFR_TEST_DIVBYZERO
|
|
/* Define to test the use of MPFR_ERRDIVZERO */
|
|
feclearexcept (FE_ALL_EXCEPT);
|
|
#endif
|
|
|
|
tests_memory_start ();
|
|
tests_rand_start ();
|
|
tests_limit_start ();
|
|
|
|
default_emin = mpfr_get_emin ();
|
|
default_emax = mpfr_get_emax ();
|
|
}
|
|
|
|
void
|
|
tests_end_mpfr (void)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mpfr_get_emin () != default_emin)
|
|
{
|
|
printf ("Default emin value has not been restored!\n");
|
|
err = 1;
|
|
}
|
|
|
|
if (mpfr_get_emax () != default_emax)
|
|
{
|
|
printf ("Default emax value has not been restored!\n");
|
|
err = 1;
|
|
}
|
|
|
|
mpfr_free_cache ();
|
|
tests_rand_end ();
|
|
tests_memory_end ();
|
|
|
|
#ifdef MPFR_TEST_DIVBYZERO
|
|
/* Define to test the use of MPFR_ERRDIVZERO */
|
|
if (fetestexcept (FE_DIVBYZERO|FE_INVALID))
|
|
{
|
|
printf ("A floating-point division by 0 or an invalid operation"
|
|
" occurred!\n");
|
|
#ifdef MPFR_ERRDIVZERO
|
|
/* This should never occur because the purpose of defining
|
|
MPFR_ERRDIVZERO is to avoid all the FP divisions by 0. */
|
|
err = 1;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
if (err)
|
|
exit (err);
|
|
}
|
|
|
|
static void
|
|
tests_limit_start (void)
|
|
{
|
|
#ifdef MPFR_TESTS_TIMEOUT
|
|
struct rlimit rlim[1];
|
|
char *timeoutp;
|
|
int timeout;
|
|
|
|
timeoutp = getenv ("MPFR_TESTS_TIMEOUT");
|
|
timeout = timeoutp != NULL ? atoi (timeoutp) : MPFR_TESTS_TIMEOUT;
|
|
if (timeout > 0)
|
|
{
|
|
/* We need to call getrlimit first to initialize rlim_max to
|
|
an acceptable value for setrlimit. When enabled, timeouts
|
|
are regarded as important: we don't want to take too much
|
|
CPU time on machines shared with other users. So, if we
|
|
can't set the timeout, we exit immediately. */
|
|
if (getrlimit (RLIMIT_CPU, rlim))
|
|
{
|
|
printf ("Error: getrlimit failed\n");
|
|
exit (1);
|
|
}
|
|
rlim->rlim_cur = timeout;
|
|
if (setrlimit (RLIMIT_CPU, rlim))
|
|
{
|
|
printf ("Error: setrlimit failed\n");
|
|
exit (1);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
tests_rand_start (void)
|
|
{
|
|
gmp_randstate_ptr rands;
|
|
char *perform_seed;
|
|
unsigned long seed;
|
|
|
|
if (__gmp_rands_initialized)
|
|
{
|
|
printf (
|
|
"Please let tests_start() initialize the global __gmp_rands, i.e.\n"
|
|
"ensure that function is called before the first use of RANDS.\n");
|
|
exit (1);
|
|
}
|
|
|
|
gmp_randinit_default (__gmp_rands);
|
|
__gmp_rands_initialized = 1;
|
|
rands = __gmp_rands;
|
|
|
|
perform_seed = getenv ("GMP_CHECK_RANDOMIZE");
|
|
if (perform_seed != NULL)
|
|
{
|
|
seed = strtoul (perform_seed, NULL, 10);
|
|
if (! (seed == 0 || seed == 1))
|
|
{
|
|
printf ("Re-seeding with GMP_CHECK_RANDOMIZE=%lu\n", seed);
|
|
gmp_randseed_ui (rands, seed);
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_GETTIMEOFDAY
|
|
struct timeval tv;
|
|
gettimeofday (&tv, NULL);
|
|
seed = tv.tv_sec + tv.tv_usec;
|
|
#else
|
|
time_t tv;
|
|
time (&tv);
|
|
seed = tv;
|
|
#endif
|
|
gmp_randseed_ui (rands, seed);
|
|
printf ("Seed GMP_CHECK_RANDOMIZE=%lu "
|
|
"(include this in bug reports)\n", seed);
|
|
}
|
|
}
|
|
else
|
|
gmp_randseed_ui (rands, 0x2143FEDC);
|
|
}
|
|
|
|
static void
|
|
tests_rand_end (void)
|
|
{
|
|
RANDS_CLEAR ();
|
|
}
|
|
|
|
/* initialization function for tests using the hardware floats
|
|
Not very useful now. */
|
|
void
|
|
mpfr_test_init (void)
|
|
{
|
|
double d;
|
|
#ifdef HAVE_FPC_CSR
|
|
/* to get denormalized numbers on IRIX64 */
|
|
union fpc_csr exp;
|
|
|
|
exp.fc_word = get_fpc_csr();
|
|
exp.fc_struct.flush = 0;
|
|
set_fpc_csr(exp.fc_word);
|
|
#endif
|
|
#ifdef HAVE_DENORMS
|
|
d = DBL_MIN;
|
|
if (2.0 * (d / 2.0) != d)
|
|
{
|
|
printf ("Error: HAVE_DENORMS defined, but no subnormals.\n");
|
|
exit (1);
|
|
}
|
|
#endif
|
|
|
|
/* generate DBL_EPSILON with a loop to avoid that the compiler
|
|
optimizes the code below in non-IEEE 754 mode, deciding that
|
|
c = d is always false. */
|
|
#if 0
|
|
for (eps = 1.0; eps != DBL_EPSILON; eps /= 2.0);
|
|
c = 1.0 + eps;
|
|
d = eps * (1.0 - eps) / 2.0;
|
|
d += c;
|
|
if (c != d)
|
|
{
|
|
printf ("Warning: IEEE 754 standard not fully supported\n"
|
|
" (maybe extended precision not disabled)\n"
|
|
" Some tests may fail\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* generate a random limb */
|
|
mp_limb_t
|
|
randlimb (void)
|
|
{
|
|
mp_limb_t limb;
|
|
|
|
mpfr_rand_raw (&limb, RANDS, GMP_NUMB_BITS);
|
|
return limb;
|
|
}
|
|
|
|
/* returns ulp(x) for x a 'normal' double-precision number */
|
|
double
|
|
Ulp (double x)
|
|
{
|
|
double y, eps;
|
|
|
|
if (x < 0) x = -x;
|
|
|
|
y = x * 2.220446049250313080847263336181640625e-16 ; /* x / 2^52 */
|
|
|
|
/* as ulp(x) <= y = x/2^52 < 2*ulp(x),
|
|
we have x + ulp(x) <= x + y <= x + 2*ulp(x),
|
|
therefore o(x + y) = x + ulp(x) or x + 2*ulp(x) */
|
|
|
|
eps = x + y;
|
|
eps = eps - x; /* ulp(x) or 2*ulp(x) */
|
|
|
|
return (eps > y) ? 0.5 * eps : eps;
|
|
}
|
|
|
|
/* returns the number of ulp's between a and b,
|
|
where a and b can be any floating-point number, except NaN
|
|
*/
|
|
int
|
|
ulp (double a, double b)
|
|
{
|
|
double twoa;
|
|
|
|
if (a == b) return 0; /* also deals with a=b=inf or -inf */
|
|
|
|
twoa = a + a;
|
|
if (twoa == a) /* a is +/-0.0 or +/-Inf */
|
|
return ((b < a) ? INT_MAX : -INT_MAX);
|
|
|
|
return (int) ((a - b) / Ulp (a));
|
|
}
|
|
|
|
/* return double m*2^e */
|
|
double
|
|
dbl (double m, int e)
|
|
{
|
|
if (e >=0 )
|
|
while (e-- > 0)
|
|
m *= 2.0;
|
|
else
|
|
while (e++ < 0)
|
|
m /= 2.0;
|
|
return m;
|
|
}
|
|
|
|
/* Warning: NaN values cannot be distinguished if MPFR_NANISNAN is defined. */
|
|
int
|
|
Isnan (double d)
|
|
{
|
|
return (d) != (d);
|
|
}
|
|
|
|
void
|
|
d_trace (const char *name, double d)
|
|
{
|
|
union {
|
|
double d;
|
|
unsigned char b[sizeof(double)];
|
|
} u;
|
|
int i;
|
|
|
|
if (name != NULL && name[0] != '\0')
|
|
printf ("%s=", name);
|
|
|
|
u.d = d;
|
|
printf ("[");
|
|
for (i = 0; i < (int) sizeof (u.b); i++)
|
|
{
|
|
if (i != 0)
|
|
printf (" ");
|
|
printf ("%02X", (int) u.b[i]);
|
|
}
|
|
printf ("] %.20g\n", d);
|
|
}
|
|
|
|
void
|
|
ld_trace (const char *name, long double ld)
|
|
{
|
|
union {
|
|
long double ld;
|
|
unsigned char b[sizeof(long double)];
|
|
} u;
|
|
int i;
|
|
|
|
if (name != NULL && name[0] != '\0')
|
|
printf ("%s=", name);
|
|
|
|
u.ld = ld;
|
|
printf ("[");
|
|
for (i = 0; i < (int) sizeof (u.b); i++)
|
|
{
|
|
if (i != 0)
|
|
printf (" ");
|
|
printf ("%02X", (int) u.b[i]);
|
|
}
|
|
printf ("] %.20Lg\n", ld);
|
|
}
|
|
|
|
/* Open a file in the src directory - can't use fopen directly */
|
|
FILE *
|
|
src_fopen (const char *filename, const char *mode)
|
|
{
|
|
#ifndef SRCDIR
|
|
return fopen (filename, mode);
|
|
#else
|
|
const char *srcdir = SRCDIR;
|
|
char *buffer;
|
|
size_t buffsize;
|
|
FILE *f;
|
|
|
|
buffsize = strlen (filename) + strlen (srcdir) + 2;
|
|
buffer = (char *) (*__gmp_allocate_func) (buffsize);
|
|
if (buffer == NULL)
|
|
{
|
|
printf ("src_fopen: failed to alloc memory)\n");
|
|
exit (1);
|
|
}
|
|
sprintf (buffer, "%s/%s", srcdir, filename);
|
|
f = fopen (buffer, mode);
|
|
(*__gmp_free_func) (buffer, buffsize);
|
|
return f;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
set_emin (mpfr_exp_t exponent)
|
|
{
|
|
if (mpfr_set_emin (exponent))
|
|
{
|
|
printf ("set_emin: setting emin to %ld failed\n", (long int) exponent);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
void
|
|
set_emax (mpfr_exp_t exponent)
|
|
{
|
|
if (mpfr_set_emax (exponent))
|
|
{
|
|
printf ("set_emax: setting emax to %ld failed\n", (long int) exponent);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
/* pos is 512 times the proportion of negative numbers.
|
|
If pos=256, half of the numbers are negative.
|
|
If pos=0, all generated numbers are positive.
|
|
*/
|
|
void
|
|
tests_default_random (mpfr_ptr x, int pos, mpfr_exp_t emin, mpfr_exp_t emax)
|
|
{
|
|
MPFR_ASSERTN (emin <= emax);
|
|
MPFR_ASSERTN (emin >= MPFR_EMIN_MIN);
|
|
MPFR_ASSERTN (emax <= MPFR_EMAX_MAX);
|
|
/* but it isn't required that emin and emax are in the current
|
|
exponent range (see below), so that underflow/overflow checks
|
|
can be done on 64-bit machines. */
|
|
|
|
mpfr_urandomb (x, RANDS);
|
|
if (MPFR_IS_PURE_FP (x) && (emin >= 1 || (randlimb () & 1)))
|
|
{
|
|
mpfr_exp_t e;
|
|
e = MPFR_GET_EXP (x) +
|
|
(emin + (long) (randlimb () % (emax - emin + 1)));
|
|
/* Note: There should be no overflow here because both terms are
|
|
between MPFR_EMIN_MIN and MPFR_EMAX_MAX, but the sum e isn't
|
|
necessarily between MPFR_EMIN_MIN and MPFR_EMAX_MAX. */
|
|
if (mpfr_set_exp (x, e))
|
|
{
|
|
/* The random number doesn't fit in the current exponent range.
|
|
In this case, test the function in the extended exponent range,
|
|
which should be restored by the caller. */
|
|
mpfr_set_emin (MPFR_EMIN_MIN);
|
|
mpfr_set_emax (MPFR_EMAX_MAX);
|
|
mpfr_set_exp (x, e);
|
|
}
|
|
}
|
|
if (randlimb () % 512 < pos)
|
|
mpfr_neg (x, x, MPFR_RNDN);
|
|
}
|
|
|
|
/* The test_one argument is seen a boolean. If it is true and rnd is
|
|
a rounding mode toward infinity, then the function is tested in
|
|
only one rounding mode (the one provided in rnd) and the variable
|
|
rndnext is not used (due to the break). If it is true and rnd is a
|
|
rounding mode toward or away from zero, then the function is tested
|
|
twice, first with the provided rounding mode and second with the
|
|
rounding mode toward the corresponding infinity (determined by the
|
|
sign of the result). If it is false, then the function is tested
|
|
in the 5 rounding modes, and rnd must initially be MPFR_RNDZ; thus
|
|
rndnext will be initialized in the first iteration.
|
|
If the test_one argument is 2, then this means that y is exact, and
|
|
the ternary value is checked.
|
|
As examples of use, see the calls to test5rm from the data_check and
|
|
bad_cases functions. */
|
|
static void
|
|
test5rm (int (*fct) (FLIST), mpfr_srcptr x, mpfr_ptr y, mpfr_ptr z,
|
|
mpfr_rnd_t rnd, int test_one, const char *name)
|
|
{
|
|
mpfr_prec_t yprec = MPFR_PREC (y);
|
|
mpfr_rnd_t rndnext = MPFR_RND_MAX; /* means uninitialized */
|
|
|
|
MPFR_ASSERTN (test_one || rnd == MPFR_RNDZ);
|
|
mpfr_set_prec (z, yprec);
|
|
while (1)
|
|
{
|
|
int inex;
|
|
|
|
MPFR_ASSERTN (rnd != MPFR_RND_MAX);
|
|
inex = fct (z, x, rnd);
|
|
if (! (mpfr_equal_p (y, z) || (mpfr_nan_p (y) && mpfr_nan_p (z))))
|
|
{
|
|
printf ("Error for %s with xprec=%lu, yprec=%lu, rnd=%s\nx = ",
|
|
name, (unsigned long) MPFR_PREC (x), (unsigned long) yprec,
|
|
mpfr_print_rnd_mode (rnd));
|
|
mpfr_out_str (stdout, 16, 0, x, MPFR_RNDN);
|
|
printf ("\nexpected ");
|
|
mpfr_out_str (stdout, 16, 0, y, MPFR_RNDN);
|
|
printf ("\ngot ");
|
|
mpfr_out_str (stdout, 16, 0, z, MPFR_RNDN);
|
|
printf ("\n");
|
|
exit (1);
|
|
}
|
|
if (test_one == 2 && inex != 0)
|
|
{
|
|
printf ("Error for %s with xprec=%lu, yprec=%lu, rnd=%s\nx = ",
|
|
name, (unsigned long) MPFR_PREC (x), (unsigned long) yprec,
|
|
mpfr_print_rnd_mode (rnd));
|
|
mpfr_out_str (stdout, 16, 0, x, MPFR_RNDN);
|
|
printf ("\nexact case, but non-zero ternary value (%d)\n", inex);
|
|
exit (1);
|
|
}
|
|
if (rnd == MPFR_RNDN)
|
|
break;
|
|
|
|
if (test_one)
|
|
{
|
|
if (rnd == MPFR_RNDU || rnd == MPFR_RNDD)
|
|
break;
|
|
|
|
if (MPFR_IS_NEG (y))
|
|
rnd = (rnd == MPFR_RNDA) ? MPFR_RNDD : MPFR_RNDU;
|
|
else
|
|
rnd = (rnd == MPFR_RNDA) ? MPFR_RNDU : MPFR_RNDD;
|
|
}
|
|
else if (rnd == MPFR_RNDZ)
|
|
{
|
|
rnd = MPFR_IS_NEG (y) ? MPFR_RNDU : MPFR_RNDD;
|
|
rndnext = MPFR_RNDA;
|
|
}
|
|
else
|
|
{
|
|
rnd = rndnext;
|
|
if (rnd == MPFR_RNDA)
|
|
{
|
|
mpfr_nexttoinf (y);
|
|
rndnext = (MPFR_IS_NEG (y)) ? MPFR_RNDD : MPFR_RNDU;
|
|
}
|
|
else if (rndnext != MPFR_RNDN)
|
|
rndnext = MPFR_RNDN;
|
|
else
|
|
{
|
|
if (yprec == MPFR_PREC_MIN)
|
|
break;
|
|
mpfr_prec_round (y, --yprec, MPFR_RNDZ);
|
|
mpfr_set_prec (z, yprec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check data in file f for function foo, with name 'name'.
|
|
Each line consists of the file f one:
|
|
|
|
xprec yprec rnd x y
|
|
|
|
where:
|
|
|
|
xprec is the input precision
|
|
yprec is the output precision
|
|
rnd is the rounding mode (n, z, u, d, a, Z, *)
|
|
x is the input (hexadecimal format)
|
|
y is the expected output (hexadecimal format) for foo(x) with rounding rnd
|
|
|
|
If rnd is Z, y is the expected output in round-toward-zero, and the
|
|
four directed rounding modes are tested, then the round-to-nearest
|
|
mode is tested in precision yprec-1. This is useful for worst cases,
|
|
where yprec is the minimum value such that one has a worst case in a
|
|
directed rounding mode.
|
|
|
|
If rnd is *, y must be an exact case. All the rounding modes are tested
|
|
and the ternary value is checked (it must be 0).
|
|
*/
|
|
void
|
|
data_check (const char *f, int (*foo) (FLIST), const char *name)
|
|
{
|
|
FILE *fp;
|
|
int xprec, yprec; /* not mpfr_prec_t because of the fscanf */
|
|
mpfr_t x, y, z;
|
|
mpfr_rnd_t rnd;
|
|
char r;
|
|
int c;
|
|
|
|
fp = fopen (f, "r");
|
|
if (fp == NULL)
|
|
fp = src_fopen (f, "r");
|
|
if (fp == NULL)
|
|
{
|
|
char *v = (char *) MPFR_VERSION_STRING;
|
|
|
|
/* In the '-dev' versions, assume that the data file exists and
|
|
return an error if the file cannot be opened to make sure
|
|
that such failures are detected. */
|
|
while (*v != '\0')
|
|
v++;
|
|
if (v[-4] == '-' && v[-3] == 'd' && v[-2] == 'e' && v[-1] == 'v')
|
|
{
|
|
printf ("Error: unable to open file '%s'\n", f);
|
|
exit (1);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
mpfr_init (x);
|
|
mpfr_init (y);
|
|
mpfr_init (z);
|
|
|
|
while (!feof (fp))
|
|
{
|
|
/* skip whitespace, for consistency */
|
|
if (fscanf (fp, " ") == EOF)
|
|
{
|
|
if (ferror (fp))
|
|
{
|
|
perror ("data_check");
|
|
exit (1);
|
|
}
|
|
else
|
|
break; /* end of file */
|
|
}
|
|
|
|
if ((c = getc (fp)) == EOF)
|
|
{
|
|
if (ferror (fp))
|
|
{
|
|
perror ("data_check");
|
|
exit (1);
|
|
}
|
|
else
|
|
break; /* end of file */
|
|
}
|
|
|
|
if (c == '#') /* comment: read entire line */
|
|
{
|
|
do
|
|
{
|
|
c = getc (fp);
|
|
}
|
|
while (!feof (fp) && c != '\n');
|
|
}
|
|
else
|
|
{
|
|
ungetc (c, fp);
|
|
|
|
c = fscanf (fp, "%d %d %c", &xprec, &yprec, &r);
|
|
MPFR_ASSERTN (xprec >= MPFR_PREC_MIN && xprec <= MPFR_PREC_MAX);
|
|
MPFR_ASSERTN (yprec >= MPFR_PREC_MIN && yprec <= MPFR_PREC_MAX);
|
|
if (c == EOF)
|
|
{
|
|
perror ("data_check");
|
|
exit (1);
|
|
}
|
|
else if (c != 3)
|
|
{
|
|
printf ("Error: corrupted line in file '%s'\n", f);
|
|
exit (1);
|
|
}
|
|
|
|
switch (r)
|
|
{
|
|
case 'n':
|
|
rnd = MPFR_RNDN;
|
|
break;
|
|
case 'z': case 'Z':
|
|
rnd = MPFR_RNDZ;
|
|
break;
|
|
case 'u':
|
|
rnd = MPFR_RNDU;
|
|
break;
|
|
case 'd':
|
|
rnd = MPFR_RNDD;
|
|
break;
|
|
case '*':
|
|
rnd = MPFR_RND_MAX; /* non-existing rounding mode */
|
|
break;
|
|
default:
|
|
printf ("Error: unexpected rounding mode"
|
|
" in file '%s': %c\n", f, (int) r);
|
|
exit (1);
|
|
}
|
|
mpfr_set_prec (x, xprec);
|
|
mpfr_set_prec (y, yprec);
|
|
if (mpfr_inp_str (x, fp, 0, MPFR_RNDN) == 0)
|
|
{
|
|
printf ("Error: corrupted argument in file '%s'\n", f);
|
|
exit (1);
|
|
}
|
|
if (mpfr_inp_str (y, fp, 0, MPFR_RNDN) == 0)
|
|
{
|
|
printf ("Error: corrupted result in file '%s'\n", f);
|
|
exit (1);
|
|
}
|
|
if (getc (fp) != '\n')
|
|
{
|
|
printf ("Error: result not followed by \\n in file '%s'\n", f);
|
|
exit (1);
|
|
}
|
|
/* Skip whitespace, in particular at the end of the file. */
|
|
if (fscanf (fp, " ") == EOF && ferror (fp))
|
|
{
|
|
perror ("data_check");
|
|
exit (1);
|
|
}
|
|
if (r == '*')
|
|
{
|
|
int rndint;
|
|
RND_LOOP (rndint)
|
|
test5rm (foo, x, y, z, (mpfr_rnd_t) rndint, 2, name);
|
|
}
|
|
else
|
|
test5rm (foo, x, y, z, rnd, r != 'Z', name);
|
|
}
|
|
}
|
|
|
|
mpfr_clear (x);
|
|
mpfr_clear (y);
|
|
mpfr_clear (z);
|
|
|
|
fclose (fp);
|
|
}
|
|
|
|
/* Test n random bad cases. A precision py in [pymin,pymax] and
|
|
* a number y of precision py are chosen randomly. One computes
|
|
* x = inv(y) in precision px = py + psup (rounded to nearest).
|
|
* Then (in general), y is a bad case for fct in precision py (in
|
|
* the directed rounding modes, but also in the rounding-to-nearest
|
|
* mode for some lower precision: see data_check).
|
|
* fct, inv, name: data related to the function.
|
|
* pos, emin, emax: arguments for tests_default_random.
|
|
*/
|
|
void
|
|
bad_cases (int (*fct)(FLIST), int (*inv)(FLIST), const char *name,
|
|
int pos, mpfr_exp_t emin, mpfr_exp_t emax,
|
|
mpfr_prec_t pymin, mpfr_prec_t pymax, mpfr_prec_t psup,
|
|
int n)
|
|
{
|
|
mpfr_t x, y, z;
|
|
char *dbgenv;
|
|
int i, dbg;
|
|
mpfr_exp_t old_emin, old_emax;
|
|
|
|
old_emin = mpfr_get_emin ();
|
|
old_emax = mpfr_get_emax ();
|
|
|
|
dbgenv = getenv ("MPFR_DEBUG_BADCASES");
|
|
dbg = dbgenv != 0 ? atoi (dbgenv) : 0; /* debug level */
|
|
mpfr_inits (x, y, z, (mpfr_ptr) 0);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
mpfr_prec_t px, py, pz;
|
|
int inex;
|
|
|
|
if (dbg)
|
|
printf ("bad_cases: i = %d\n", i);
|
|
py = pymin + (randlimb () % (pymax - pymin + 1));
|
|
mpfr_set_prec (y, py);
|
|
tests_default_random (y, pos, emin, emax);
|
|
if (dbg)
|
|
{
|
|
printf ("bad_cases: yprec =%4ld, y = ", (long) py);
|
|
mpfr_out_str (stdout, 16, 0, y, MPFR_RNDN);
|
|
printf ("\n");
|
|
}
|
|
px = py + psup;
|
|
mpfr_set_prec (x, px);
|
|
mpfr_clear_flags ();
|
|
inv (x, y, MPFR_RNDN);
|
|
if (mpfr_nanflag_p () || mpfr_overflow_p () || mpfr_underflow_p ())
|
|
{
|
|
if (dbg)
|
|
printf ("bad_cases: no normal inverse\n");
|
|
goto next_i;
|
|
}
|
|
if (dbg > 1)
|
|
{
|
|
printf ("bad_cases: x = ");
|
|
mpfr_out_str (stdout, 16, 0, x, MPFR_RNDN);
|
|
printf ("\n");
|
|
}
|
|
pz = px;
|
|
do
|
|
{
|
|
pz += 32;
|
|
mpfr_set_prec (z, pz);
|
|
if (fct (z, x, MPFR_RNDN) == 0)
|
|
{
|
|
if (dbg)
|
|
printf ("bad_cases: exact case\n");
|
|
goto next_i;
|
|
}
|
|
if (dbg)
|
|
{
|
|
if (dbg > 1)
|
|
{
|
|
printf ("bad_cases: %s(x) ~= ", name);
|
|
mpfr_out_str (stdout, 16, 0, z, MPFR_RNDN);
|
|
}
|
|
else
|
|
{
|
|
printf ("bad_cases: [MPFR_RNDZ] ~= ");
|
|
mpfr_out_str (stdout, 16, 40, z, MPFR_RNDZ);
|
|
}
|
|
printf ("\n");
|
|
}
|
|
inex = mpfr_prec_round (z, py, MPFR_RNDN);
|
|
if (mpfr_nanflag_p () || mpfr_overflow_p () || mpfr_underflow_p ()
|
|
|| ! mpfr_equal_p (z, y))
|
|
{
|
|
if (dbg)
|
|
printf ("bad_cases: inverse doesn't match\n");
|
|
goto next_i;
|
|
}
|
|
}
|
|
while (inex == 0);
|
|
/* We really have a bad case. */
|
|
do
|
|
py--;
|
|
while (py >= MPFR_PREC_MIN && mpfr_prec_round (z, py, MPFR_RNDZ) == 0);
|
|
py++;
|
|
/* py is now the smallest output precision such that we have
|
|
a bad case in the directed rounding modes. */
|
|
if (mpfr_prec_round (y, py, MPFR_RNDZ) != 0)
|
|
{
|
|
printf ("Internal error for i = %d\n", i);
|
|
exit (1);
|
|
}
|
|
if ((inex > 0 && MPFR_IS_POS (z)) ||
|
|
(inex < 0 && MPFR_IS_NEG (z)))
|
|
{
|
|
mpfr_nexttozero (y);
|
|
if (mpfr_zero_p (y))
|
|
goto next_i;
|
|
}
|
|
if (dbg)
|
|
{
|
|
printf ("bad_cases: yprec =%4ld, y = ", (long) py);
|
|
mpfr_out_str (stdout, 16, 0, y, MPFR_RNDN);
|
|
printf ("\n");
|
|
}
|
|
/* Note: y is now the expected result rounded toward zero. */
|
|
test5rm (fct, x, y, z, MPFR_RNDZ, 0, name);
|
|
next_i:
|
|
/* In case the exponent range has been changed by
|
|
tests_default_random()... */
|
|
mpfr_set_emin (old_emin);
|
|
mpfr_set_emax (old_emax);
|
|
}
|
|
mpfr_clears (x, y, z, (mpfr_ptr) 0);
|
|
}
|
|
|
|
void
|
|
flags_out (unsigned int flags)
|
|
{
|
|
int none = 1;
|
|
|
|
if (flags & MPFR_FLAGS_UNDERFLOW)
|
|
none = 0, printf (" underflow");
|
|
if (flags & MPFR_FLAGS_OVERFLOW)
|
|
none = 0, printf (" overflow");
|
|
if (flags & MPFR_FLAGS_NAN)
|
|
none = 0, printf (" nan");
|
|
if (flags & MPFR_FLAGS_INEXACT)
|
|
none = 0, printf (" inexact");
|
|
if (flags & MPFR_FLAGS_ERANGE)
|
|
none = 0, printf (" erange");
|
|
if (none)
|
|
printf (" none");
|
|
printf (" (%u)\n", flags);
|
|
}
|