summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4>2020-09-28 09:55:56 +0000
committervlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4>2020-09-28 09:55:56 +0000
commitb7e3380efb04fed5fd74d556c2258334d7190a88 (patch)
tree62511cc1dff0f9373d6aeb852cc11cc68e7c7738
parentf4fb535442aa4bf07e59195d306820d56ebfc3fb (diff)
downloadmpfr-b7e3380efb04fed5fd74d556c2258334d7190a88.tar.gz
[src/set_z_exp.c] Fixed overflow/underflow detection in exponent ranges
where emax < 0 or emin >= 0 (bug introduced in r14147). [tests/tset_z_exp.c] Added some tests in a reduced exponent range for the bug fixed here (and improved existing tests). Note: this bug was detected with mini-gmp (for which mpfr_set_z_2exp is used more often because a long does not fit into a limb). (merged changesets r14146-14150 of src/set_z_2exp.c and tests/tset_z_2exp.c from the trunk) git-svn-id: https://scm.gforge.inria.fr/anonscm/svn/mpfr/branches/4.1@14151 280ebfd0-de03-0410-8827-d642c229c3f4
-rw-r--r--src/set_z_exp.c27
-rw-r--r--tests/tset_z_exp.c93
2 files changed, 79 insertions, 41 deletions
diff --git a/src/set_z_exp.c b/src/set_z_exp.c
index 67e58510b..9fdf8a58b 100644
--- a/src/set_z_exp.c
+++ b/src/set_z_exp.c
@@ -78,23 +78,18 @@ mpfr_set_z_2exp (mpfr_ptr f, mpz_srcptr z, mpfr_exp_t e, mpfr_rnd_t rnd_mode)
result in modular arithmetic, using mpfr_uexp_t, and convert it to
mpfr_exp_t. */
uexp = (mpfr_uexp_t) zn * GMP_NUMB_BITS + (mpfr_uexp_t) e - k;
+
+ /* Convert to signed in a portable way (see doc/README.dev).
+ On most platforms, this can be optimized to identity (no-op). */
+ exp = uexp > MPFR_EXP_MAX ? -1 - (mpfr_exp_t) ~uexp : (mpfr_exp_t) uexp;
+
/* The exponent will be exp or exp + 1 (due to rounding) */
- if (uexp <= MPFR_EXP_MAX) /* non-negative */
- {
- if (MPFR_UNLIKELY (uexp > __gmpfr_emax))
- return mpfr_overflow (f, rnd_mode, sign_z);
- exp = uexp;
- }
- else /* negative */
- {
- /* Convert to signed in a portable way (see doc/README.dev).
- On most platforms, this can be optimized to identity (no-op). */
- exp = -1 - (mpfr_exp_t) ~uexp;
- if (MPFR_UNLIKELY (exp + 1 < __gmpfr_emin))
- return mpfr_underflow (f,
- rnd_mode == MPFR_RNDN ? MPFR_RNDZ : rnd_mode,
- sign_z);
- }
+
+ if (MPFR_UNLIKELY (exp > __gmpfr_emax))
+ return mpfr_overflow (f, rnd_mode, sign_z);
+ if (MPFR_UNLIKELY (exp + 1 < __gmpfr_emin))
+ return mpfr_underflow (f, rnd_mode == MPFR_RNDN ? MPFR_RNDZ : rnd_mode,
+ sign_z);
if (MPFR_LIKELY (dif >= 0))
{
diff --git a/tests/tset_z_exp.c b/tests/tset_z_exp.c
index ac26c4b06..7f8fe523a 100644
--- a/tests/tset_z_exp.c
+++ b/tests/tset_z_exp.c
@@ -97,46 +97,89 @@ check0 (void)
mpfr_get_si is a rather indirect test of a low level routine. */
static void
-check (long i, mpfr_rnd_t rnd)
+check (long i, mpfr_rnd_t rnd, int reduced)
{
- mpfr_t f;
+ mpfr_t f1, f2, f3;
mpz_t z;
- mpfr_exp_t e;
+ mpfr_exp_t e, old_emin, old_emax;
int inex;
+ mpfr_flags_t flags;
+
+ old_emin = mpfr_get_emin ();
+ old_emax = mpfr_get_emax ();
/* using CHAR_BIT * sizeof(long) bits of precision ensures that
mpfr_set_z_2exp is exact below */
- mpfr_init2 (f, CHAR_BIT * sizeof(long));
+ mpfr_inits2 (CHAR_BIT * sizeof(long), f1, f2, f3, (mpfr_ptr) 0);
mpz_init (z);
mpz_set_ui (z, i);
/* the following loop ensures that no overflow occurs */
do
e = randexp ();
while (e > mpfr_get_emax () - CHAR_BIT * sizeof(long));
- inex = mpfr_set_z_2exp (f, z, e, rnd);
- if (inex != 0)
+
+ mpfr_clear_flags ();
+ inex = mpfr_set_z_2exp (f1, z, e, rnd);
+ flags = __gmpfr_flags;
+
+ if (inex != 0 || flags != 0 ||
+ (mpfr_div_2si (f2, f1, e, rnd), mpfr_get_si (f2, MPFR_RNDZ) != i))
{
- printf ("Error in mpfr_set_z_2exp for i=%ld, e=%ld,"
- " wrong ternary value\n", i, (long) e);
- printf ("expected 0, got %d\n", inex);
+ printf ("Error in mpfr_set_z_2exp for i=%ld e=%" MPFR_EXP_FSPEC
+ "d rnd_mode=%d\n", i, (mpfr_eexp_t) e, rnd);
+ mpfr_set_si_2exp (f2, i, e, MPFR_RNDN);
+ printf ("expected "); mpfr_dump (f2);
+ printf ("with inex = %d and flags =", 0);
+ flags_out (0);
+ printf ("got "); mpfr_dump (f1);
+ printf ("with inex = %d and flags =", inex);
+ flags_out (flags);
exit (1);
}
- mpfr_div_2si (f, f, e, rnd);
- if (mpfr_get_si (f, MPFR_RNDZ) != i)
+
+ if (reduced)
{
- printf ("Error in mpfr_set_z_2exp for i=%ld e=", i);
- if (e < LONG_MIN)
- printf ("(<LONG_MIN)");
- else if (e > LONG_MAX)
- printf ("(>LONG_MAX)");
- else
- printf ("%ld", (long) e);
- printf (" rnd_mode=%d\n", rnd);
- printf ("expected %ld\n", i);
- printf ("got "); mpfr_dump (f);
- exit (1);
+ mpfr_exp_t ef, emin, emax;
+ int inex2, inex3;
+ mpfr_flags_t flags2, flags3;
+
+ ef = i == 0 ? 0 : mpfr_get_exp (f1);
+ for (emin = ef - 2; emin <= ef + 2; emin++)
+ for (emax = emin; emax <= ef + 2; emax++)
+ {
+ inex3 = mpfr_set (f3, f1, rnd);
+ MPFR_ASSERTN (inex3 == 0);
+ mpfr_set_emin (emin);
+ mpfr_set_emax (emax);
+ mpfr_clear_flags ();
+ inex2 = mpfr_set_z_2exp (f2, z, e, rnd);
+ flags2 = __gmpfr_flags;
+ mpfr_clear_flags ();
+ inex3 = mpfr_check_range (f3, 0, rnd);
+ flags3 = __gmpfr_flags;
+ if (!(mpfr_equal_p (f2, f3) &&
+ SAME_SIGN (inex2, inex3) &&
+ flags2 == flags3))
+ {
+ printf ("Error in mpfr_set_z_2exp for i=%ld e=%"
+ MPFR_EXP_FSPEC "d rnd_mode=%d\nand emin=%"
+ MPFR_EXP_FSPEC "d emax=%" MPFR_EXP_FSPEC
+ "d\n", i, (mpfr_eexp_t) e, rnd,
+ (mpfr_eexp_t) emin, (mpfr_eexp_t) emax);
+ printf ("expected "); mpfr_dump (f3);
+ printf ("with inex = %d and flags =", inex3);
+ flags_out (flags3);
+ printf ("got "); mpfr_dump (f2);
+ printf ("with inex = %d and flags =", inex2);
+ flags_out (flags2);
+ exit (1);
+ }
+ }
+ mpfr_set_emin (old_emin);
+ mpfr_set_emax (old_emax);
}
- mpfr_clear (f);
+
+ mpfr_clears (f1, f2, f3, (mpfr_ptr) 0);
mpz_clear (z);
}
@@ -204,9 +247,9 @@ main (int argc, char *argv[])
tests_start_mpfr ();
- check (0, MPFR_RNDN);
+ check (0, MPFR_RNDN, 0);
for (j = 0; j < 200000; j++)
- check (randlimb () & LONG_MAX, RND_RAND ());
+ check (randlimb () & LONG_MAX, RND_RAND (), j < 200);
check0 ();
check_huge ();