diff options
author | Stefan Krah <skrah@bytereef.org> | 2012-04-18 16:59:56 (GMT) |
---|---|---|
committer | Stefan Krah <skrah@bytereef.org> | 2012-04-18 16:59:56 (GMT) |
commit | 5d0d2e2b04631e5ad9c7dad60e6be75c02f8e050 (patch) | |
tree | 3c11d6898a03ce23ca5b3b4c7eebc56ad5e17acb /Modules | |
parent | ed4b21ff4f79dbd7461afa6739bfc1970ceee2d6 (diff) | |
download | cpython-5d0d2e2b04631e5ad9c7dad60e6be75c02f8e050.zip cpython-5d0d2e2b04631e5ad9c7dad60e6be75c02f8e050.tar.gz cpython-5d0d2e2b04631e5ad9c7dad60e6be75c02f8e050.tar.bz2 |
Explain the strategy to avoid huge alignment shifts in _mpd_qadd() in detail.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_decimal/libmpdec/mpdecimal.c | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c index bad66d8..6dd7002 100644 --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -3103,14 +3103,43 @@ _mpd_qaddsub(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, _mpd_ptrswap(&big, &small); swap++; } + /* align the coefficients */ if (!mpd_iszerocoeff(big)) { - /* Test for adjexp(small) + big->digits < adjexp(big), if big-digits > prec - * Test for adjexp(small) + prec + 1 < adjexp(big), if big-digits <= prec - * If true, the magnitudes of the numbers are so far apart that one can as - * well add or subtract 1*10**big->exp. */ exp = big->exp - 1; exp += (big->digits > ctx->prec) ? 0 : big->digits-ctx->prec-1; if (mpd_adjexp(small) < exp) { + /* + * Avoid huge shifts by substituting a value for small that is + * guaranteed to produce the same results. + * + * adjexp(small) < exp if and only if: + * + * bdigits <= prec AND + * bdigits+shift >= prec+2+sdigits AND + * exp = bexp+bdigits-prec-2 + * + * 1234567000000000 -> bdigits + shift + * ----------XX1234 -> sdigits + * ----------X1 -> tiny-digits + * |- prec -| + * + * OR + * + * bdigits > prec AND + * shift > sdigits AND + * exp = bexp-1 + * + * 1234567892100000 -> bdigits + shift + * ----------XX1234 -> sdigits + * ----------X1 -> tiny-digits + * |- prec -| + * + * If tiny is zero, adding or subtracting is a no-op. + * Otherwise, adding tiny generates a non-zero digit either + * below the rounding digit or the least significant digit + * of big. When subtracting, tiny is in the same position as + * the carry that would be generated by subtracting sdigits. + */ mpd_copy_flags(&tiny, small); tiny.exp = exp; tiny.digits = 1; @@ -3118,7 +3147,7 @@ _mpd_qaddsub(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, tiny.data[0] = mpd_iszerocoeff(small) ? 0 : 1; small = &tiny; } - /* this cannot wrap: the difference is positive and <= maxprec+1 */ + /* This cannot wrap: the difference is positive and <= maxprec */ shift = big->exp - small->exp; if (!mpd_qshiftl(&big_aligned, big, shift, status)) { mpd_seterror(result, MPD_Malloc_error, status); @@ -3521,7 +3550,7 @@ mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, /* Internal function. */ static void _mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) + const mpd_context_t *ctx, uint32_t *status) { MPD_NEW_STATIC(aligned,0,0,0,0); mpd_ssize_t qsize, rsize; |