From ad5b43995e758c7a1f81ce6cf2cd798b48712808 Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Wed, 22 Aug 2012 19:11:50 +0200 Subject: In the 32-bit build, dec_hash() raised InvalidOperation if the operand had a coefficient with MAX_PREC=425000000 digits and a negative exponent. Increasing the context limits above the official values fixes the issue and is safe (in this case!). --- Modules/_decimal/_decimal.c | 16 ++++++++++---- Modules/_decimal/tests/bignum.py | 45 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 Modules/_decimal/tests/bignum.py diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index ad84d58..6217a3f 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -4338,6 +4338,11 @@ _dec_hash(PyDecObject *v) } tmp->exp = 0; mpd_set_positive(tmp); + + maxctx.prec = MPD_MAX_PREC + 21; + maxctx.emax = MPD_MAX_EMAX + 21; + maxctx.emin = MPD_MIN_EMIN - 21; + mpd_qmul(tmp, tmp, exp_hash, &maxctx, &status); mpd_qrem(tmp, tmp, &p, &maxctx, &status); @@ -4346,11 +4351,14 @@ _dec_hash(PyDecObject *v) result = (result == -1) ? -2 : result; if (status != 0) { - status |= MPD_Invalid_operation; - if (dec_addstatus(context, status)) { - result = -1; - goto finish; + if (status & MPD_Malloc_error) { + goto malloc_error; + } + else { + PyErr_SetString(PyExc_RuntimeError, + "dec_hash: internal error: please report"); } + result = -1; } diff --git a/Modules/_decimal/tests/bignum.py b/Modules/_decimal/tests/bignum.py new file mode 100644 index 0000000..9e9e769 --- /dev/null +++ b/Modules/_decimal/tests/bignum.py @@ -0,0 +1,45 @@ +# +# These tests require gmpy and test the limits of the 32-bit build. The +# limits of the 64-bit build are so large that they cannot be tested +# on accessible hardware. +# + +import sys +from decimal import * +from gmpy import mpz + + +_PyHASH_MODULUS = sys.hash_info.modulus +# hash values to use for positive and negative infinities, and nans +_PyHASH_INF = sys.hash_info.inf +_PyHASH_NAN = sys.hash_info.nan + +# _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS +_PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS) + +def xhash(coeff, exp): + sign = 1 + if coeff < 0: + sign = -1 + coeff = -coeff + if exp >= 0: + exp_hash = pow(10, exp, _PyHASH_MODULUS) + else: + exp_hash = pow(_PyHASH_10INV, -exp, _PyHASH_MODULUS) + hash_ = coeff * exp_hash % _PyHASH_MODULUS + ans = hash_ if sign == 1 else -hash_ + return -2 if ans == -1 else ans + + +x = mpz(10) ** 425000000 - 1 +coeff = int(x) + +d = Decimal('9' * 425000000 + 'e-849999999') + +h1 = xhash(coeff, -849999999) +h2 = hash(d) + +assert h2 == h1 + + + -- cgit v0.12