]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
In the 32-bit build, dec_hash() raised InvalidOperation if the operand
authorStefan Krah <skrah@bytereef.org>
Wed, 22 Aug 2012 17:11:50 +0000 (19:11 +0200)
committerStefan Krah <skrah@bytereef.org>
Wed, 22 Aug 2012 17:11:50 +0000 (19:11 +0200)
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
Modules/_decimal/tests/bignum.py [new file with mode: 0644]

index ad84d58a8745300a48dd0162b67f20181b6cb86d..6217a3f556334a20cab070ef8c1138ae225bef39 100644 (file)
@@ -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 (file)
index 0000000..9e9e769
--- /dev/null
@@ -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
+
+
+