summaryrefslogtreecommitdiff
path: root/src/sub1.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sub1.c')
-rw-r--r--src/sub1.c27
1 files changed, 17 insertions, 10 deletions
diff --git a/src/sub1.c b/src/sub1.c
index c38e2f979..f67fcd9f0 100644
--- a/src/sub1.c
+++ b/src/sub1.c
@@ -34,7 +34,7 @@ int
mpfr_sub1 (mpfr_ptr a, mpfr_srcptr b, mpfr_srcptr c, mpfr_rnd_t rnd_mode)
{
int sign;
- mpfr_exp_t diff_exp, exp_b;
+ mpfr_exp_t diff_exp, exp_a, exp_b;
mpfr_prec_t cancel, cancel1;
mp_size_t cancel2, an, bn, cn, cn0;
mp_limb_t *ap, *bp, *cp;
@@ -652,15 +652,19 @@ mpfr_sub1 (mpfr_ptr a, mpfr_srcptr b, mpfr_srcptr c, mpfr_rnd_t rnd_mode)
/* we have to set MPFR_EXP(a) to MPFR_EXP(b) - cancel + add_exp, taking
care of underflows/overflows in that computation, and of the allowed
exponent range */
+ MPFR_TMP_FREE (marker);
if (MPFR_LIKELY(cancel))
{
- mpfr_exp_t exp_a;
-
cancel -= add_exp; /* OK: add_exp is an int equal to 0 or 1 */
exp_a = exp_b - cancel;
+ /* The following assertion corresponds to a limitation of the MPFR
+ implementation. It may fail with a 32-bit ABI and huge precisions,
+ but this is practically impossible with a 64-bit ABI. This kind
+ of issue is not specific to this function. */
+ MPFR_ASSERTN (exp_b != MPFR_EXP_MAX || exp_a > __gmpfr_emax);
if (MPFR_UNLIKELY (exp_a < __gmpfr_emin))
{
- MPFR_TMP_FREE (marker);
+ underflow:
if (rnd_mode == MPFR_RNDN &&
(exp_a < __gmpfr_emin - 1 ||
(inexact >= 0 && mpfr_powerof2_raw (a))))
@@ -669,25 +673,28 @@ mpfr_sub1 (mpfr_ptr a, mpfr_srcptr b, mpfr_srcptr c, mpfr_rnd_t rnd_mode)
}
if (MPFR_UNLIKELY (exp_a > __gmpfr_emax))
{
- MPFR_TMP_FREE (marker);
return mpfr_overflow (a, rnd_mode, MPFR_SIGN (a));
}
- MPFR_SET_EXP (a, exp_a);
}
else /* cancel = 0: MPFR_EXP(a) <- MPFR_EXP(b) + add_exp */
{
+
/* in case cancel = 0, add_exp can still be 1, in case b is just
below a power of two, c is very small, prec(a) < prec(b),
and rnd=away or nearest */
MPFR_ASSERTD (add_exp == 0 || add_exp == 1);
- if (MPFR_UNLIKELY (add_exp && exp_b >= __gmpfr_emax))
+ /* Overflow iff exp_b + add_exp > __gmpfr_emax in Z, but we do
+ a subtraction below to avoid a potential integer overflow in
+ the case exp_b == MPFR_EXP_MAX. */
+ if (MPFR_UNLIKELY (exp_b > __gmpfr_emax - add_exp))
{
- MPFR_TMP_FREE (marker);
return mpfr_overflow (a, rnd_mode, MPFR_SIGN (a));
}
- MPFR_SET_EXP (a, exp_b + add_exp);
+ exp_a = exp_b + add_exp;
+ if (MPFR_UNLIKELY (exp_a < __gmpfr_emin))
+ goto underflow;
}
- MPFR_TMP_FREE(marker);
+ MPFR_SET_EXP (a, exp_a);
/* check that result is msb-normalized */
MPFR_ASSERTD(ap[an-1] > ~ap[an-1]);
MPFR_RET (inexact * MPFR_INT_SIGN (a));