diff -Naur mpfr-2.1.1-p6/div_ui.c mpfr-2.1.1-p7/div_ui.c --- mpfr-2.1.1-p6/div_ui.c 2004-03-26 13:27:15.000000000 +0000 +++ mpfr-2.1.1-p7/div_ui.c 2005-05-04 15:55:58.183515000 +0000 @@ -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 @@ 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; } + else + { + count_leading_zeros (sh, tmp[yn]); - /* now we have yn limbs starting from tmp[1], with tmp[yn]<>0 */ + /* 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 ? + */ - /* shift left to normalize */ - count_leading_zeros(sh, tmp[yn]); - if (sh) - { - 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; + 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 @@ 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 @@ } 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)); diff -Naur mpfr-2.1.1-p6/tests/tdiv_ui.c mpfr-2.1.1-p7/tests/tdiv_ui.c --- mpfr-2.1.1-p6/tests/tdiv_ui.c 2005-01-27 17:22:35.000000000 +0000 +++ mpfr-2.1.1-p7/tests/tdiv_ui.c 2005-05-04 16:02:25.803493000 +0000 @@ -219,6 +219,20 @@ } mpfr_clear(x); + mpfr_init2 (x, 32); + mpfr_set_str (x, "1.0000000000000000000000000000111e-2", 2, GMP_RNDD); + mpfr_div_ui (x, x, 275604255, GMP_RNDN); + if (mpfr_cmp_str (x, "1.1111001010101110101010001101111e-31", 2, GMP_RNDD)) + { + printf ("Error for x=1.0000000000000000000000000000111e-2,\n" + "u=275604255, prec=32, rnd_mode=GMP_RNDN\n" + "got "); + mpfr_out_str (stdout, 2, 0, x, GMP_RNDD); + printf ("\nexpected 1.1111001010101110101010001101111e-31\n"); + exit (1); + } + mpfr_clear(x); + tests_end_mpfr (); return 0; }