summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/fma.c8
-rw-r--r--tests/tfma.c24
2 files changed, 29 insertions, 3 deletions
diff --git a/src/fma.c b/src/fma.c
index 11491fc55..6416cd307 100644
--- a/src/fma.c
+++ b/src/fma.c
@@ -234,7 +234,7 @@ mpfr_fma (mpfr_ptr s, mpfr_srcptr x, mpfr_srcptr y, mpfr_srcptr z,
/* Let's eliminate the obvious case where x*y and z have the
same sign. No possible cancellation -> real overflow.
Also, we know that |z| < 2^emax. If E(x) + E(y) >= emax+3,
- then |x*y| >= 2^(emax+1), and |x*y + z| >= 2^emax. This case
+ then |x*y| >= 2^(emax+1), and |x*y + z| > 2^emax. This case
is also an overflow. */
if (MPFR_SIGN (u) == MPFR_SIGN (z) || e >= __gmpfr_emax + 3)
{
@@ -253,7 +253,8 @@ mpfr_fma (mpfr_ptr s, mpfr_srcptr x, mpfr_srcptr y, mpfr_srcptr z,
inexact = mpfr_mul (u, u, y, MPFR_RNDN);
MPFR_ASSERTN (inexact == 0);
- /* Now, we need to add z/4... But it may underflow! */
+ /* Now, we need to add z/4, which is in fact a subtraction since
+ x*y and z have different signs. But it may underflow! */
{
mpfr_t zo4;
mpfr_srcptr zz;
@@ -262,7 +263,8 @@ mpfr_fma (mpfr_ptr s, mpfr_srcptr x, mpfr_srcptr y, mpfr_srcptr z,
if (MPFR_GET_EXP (u) > MPFR_GET_EXP (z) &&
MPFR_GET_EXP (u) - MPFR_GET_EXP (z) > MPFR_PREC (u))
{
- /* |z| < ulp(u)/2, therefore one can use z instead of z/4. */
+ /* |z| < ulp(u)/2, therefore one can use z instead of z/4,
+ except maybe when u is a power of 2! */
zz = z;
}
else
diff --git a/tests/tfma.c b/tests/tfma.c
index f3a3b30e7..b8918a192 100644
--- a/tests/tfma.c
+++ b/tests/tfma.c
@@ -196,6 +196,29 @@ test_overflow2 (void)
}
static void
+test_overflow3 (void)
+{
+ mpfr_t x, y, z, r;
+ int inex;
+ mpfr_prec_t p = 8;
+
+ mpfr_inits2 (p, x, y, z, (mpfr_ptr) 0);
+ mpfr_init2 (r, 2 * p);
+ mpfr_set_ui_2exp (x, 1, mpfr_get_emax () - 1, MPFR_RNDN);
+ mpfr_set_ui (y, 2, MPFR_RNDN); /* y = 2 */
+ mpfr_set_si_2exp (z, -1, mpfr_get_emax () - 2*p - 2, MPFR_RNDN);
+ mpfr_nextabove (z);
+ mpfr_clear_flags ();
+ /* We have x*y = 2^emax and z = -2^(emax-2p-2)*(1-2^(-p)) thus
+ x*y+z = 2^emax - 2^(emax-2p-2) + 2^(emax-3p-2) should overflow,
+ since it is closest from 2^emax than from 2^emax - 2^(emax-2p). */
+ inex = mpfr_fma (r, x, y, z, MPFR_RNDN);
+ MPFR_ASSERTN(inex > 0);
+ MPFR_ASSERTN(mpfr_inf_p (r) && mpfr_sgn (r) > 0);
+ mpfr_clears (x, y, z, r, (mpfr_ptr) 0);
+}
+
+static void
test_underflow1 (void)
{
mpfr_t x, y, z, r;
@@ -833,6 +856,7 @@ main (int argc, char *argv[])
set_emax (MPFR_EMAX_MAX);
test_overflow1 ();
test_overflow2 ();
+ test_overflow3 ();
test_underflow1 ();
test_underflow2 ();
test_underflow3 (2);