summaryrefslogtreecommitdiff
path: root/div_ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'div_ui.c')
-rw-r--r--div_ui.c87
1 files changed, 63 insertions, 24 deletions
diff --git a/div_ui.c b/div_ui.c
index 052f9c9f2..8faffc7f4 100644
--- a/div_ui.c
+++ b/div_ui.c
@@ -1,6 +1,7 @@
/* mpfr_div_ui -- divide a floating-point number by a machine integer
-Copyright 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Free Software Foundation, Inc.
This file is part of the MPFR Library.
@@ -96,40 +97,75 @@ mpfr_div_ui (mpfr_ptr y, mpfr_srcptr x, unsigned long int u, mp_rnd_t rnd_mode)
c = mpn_divrem_1 (tmp, 0, xp - dif, yn + 1, c);
inexact = (c != 0);
+
+ /* First pass in estimating next bit of the quotient, in case of RNDN *
+ * In case we just have the right number of bits (postpone this ?), *
+ * we need to check whether the remainder is more or less than half *
+ * the divisor. The test must be performed with a subtraction, so as *
+ * to prevent carries. */
+
if (rnd_mode == GMP_RNDN)
{
- if (2 * c < (mp_limb_t) u)
+ if (c < (mp_limb_t) u - c) /* We have u > c */
middle = -1;
- else if (2 * c > (mp_limb_t) u)
+ else if (c > (mp_limb_t) u - c)
middle = 1;
else
middle = 0; /* exactly in the middle */
}
+
+ /* If we believe that we are right in the middle or exact, we should check
+ that we did not neglect any word of x (division large / 1 -> small). */
+
for (i=0; ((inexact == 0) || (middle == 0)) && (i < -dif); i++)
if (xp[i])
inexact = middle = 1; /* larger than middle */
- if (tmp[yn] == 0) /* high limb is zero */
+ /*
+ If the high limb of the result is 0 (xp[xn-1] < u), remove it.
+ Otherwise, compute the left shift to be performed to normalize.
+ In the latter case, we discard some low bits computed. They
+ contain information useful for the rounding, hence the updating
+ of middle and inexact.
+ */
+
+ if (tmp[yn] == 0)
{
- tmp--;
- sh = 0;
+ MPN_COPY(yp, tmp, yn);
exp -= BITS_PER_MP_LIMB;
+ sh = 0;
}
-
- /* now we have yn limbs starting from tmp[1], with tmp[yn]<>0 */
-
- /* shift left to normalize */
- count_leading_zeros(sh, tmp[yn]);
- if (sh)
+ else
{
- mpn_lshift (yp, tmp + 1, yn, sh);
- yp[0] += tmp[0] >> (BITS_PER_MP_LIMB - sh);
- middle = middle || ((tmp[0] << sh) != 0);
- inexact = inexact || ((tmp[0] << sh) != 0);
- exp -= sh;
+ count_leading_zeros (sh, tmp[yn]);
+
+ /* shift left to normalize */
+ if (sh)
+ {
+ mp_limb_t w = tmp[0] << sh;
+
+ mpn_lshift (yp, tmp + 1, yn, sh);
+ yp[0] += tmp[0] >> (BITS_PER_MP_LIMB - sh);
+
+ if (w > (MPFR_LIMB_ONE << (BITS_PER_MP_LIMB - 1)))
+ { middle = 1; }
+ else if (w < (MPFR_LIMB_ONE << (BITS_PER_MP_LIMB - 1)))
+ { middle = -1; }
+ else
+ { middle = (c != 0); }
+
+ inexact = inexact || (w != 0);
+ exp -= sh;
+ }
+ else
+ { /* this happens only if u == 1 and xp[xn-1] >=
+ 1<<(BITS_PER_MP_LIMB-1). It might be better to handle the
+ u == 1 case seperately ?
+ */
+
+ MPN_COPY (yp, tmp + 1, yn);
+ }
}
- else
- MPN_COPY(yp, tmp + 1, yn);
sh = yn * BITS_PER_MP_LIMB - MPFR_PREC(y);
/* it remains sh bits in less significant limb of y */
@@ -160,6 +196,7 @@ mpfr_div_ui (mpfr_ptr y, mpfr_srcptr x, unsigned long int u, mp_rnd_t rnd_mode)
default:
MPFR_ASSERTD(rnd_mode == GMP_RNDN);
+ /* we have one more significant bit in yn */
if (sh && d < (MPFR_LIMB_ONE << (sh - 1)))
MPFR_RET(-MPFR_INT_SIGN(x));
else if (sh && d > (MPFR_LIMB_ONE << (sh - 1)))
@@ -169,12 +206,14 @@ mpfr_div_ui (mpfr_ptr y, mpfr_srcptr x, unsigned long int u, mp_rnd_t rnd_mode)
}
else /* sh = 0 or d = 1 << (sh-1) */
{
- /* we are in the middle if:
- (a) sh > 0 and inexact == 0
- (b) sh=0 and middle=1
- */
+ /* The first case is "false" even rounding (significant bits
+ indicate even rounding, but the result is inexact, so up) ;
+ The second case is the case where middle should be used to
+ decide the direction of rounding (no further bit computed) ;
+ The third is the true even rounding.
+ */
if ((sh && inexact) || (!sh && (middle > 0)) ||
- (*yp & (MPFR_LIMB_ONE << sh)))
+ (!inexact && *yp & (MPFR_LIMB_ONE << sh)))
{
mpfr_add_one_ulp (y, rnd_mode);
MPFR_RET(MPFR_INT_SIGN(x));