Skip to content

Commit ad5b439

Browse files
author
Stefan Krah
committed
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!).
1 parent 2fd502f commit ad5b439

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

Modules/_decimal/_decimal.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4338,6 +4338,11 @@ _dec_hash(PyDecObject *v)
43384338
}
43394339
tmp->exp = 0;
43404340
mpd_set_positive(tmp);
4341+
4342+
maxctx.prec = MPD_MAX_PREC + 21;
4343+
maxctx.emax = MPD_MAX_EMAX + 21;
4344+
maxctx.emin = MPD_MIN_EMIN - 21;
4345+
43414346
mpd_qmul(tmp, tmp, exp_hash, &maxctx, &status);
43424347
mpd_qrem(tmp, tmp, &p, &maxctx, &status);
43434348

@@ -4346,11 +4351,14 @@ _dec_hash(PyDecObject *v)
43464351
result = (result == -1) ? -2 : result;
43474352

43484353
if (status != 0) {
4349-
status |= MPD_Invalid_operation;
4350-
if (dec_addstatus(context, status)) {
4351-
result = -1;
4352-
goto finish;
4354+
if (status & MPD_Malloc_error) {
4355+
goto malloc_error;
4356+
}
4357+
else {
4358+
PyErr_SetString(PyExc_RuntimeError,
4359+
"dec_hash: internal error: please report");
43534360
}
4361+
result = -1;
43544362
}
43554363

43564364

Modules/_decimal/tests/bignum.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#
2+
# These tests require gmpy and test the limits of the 32-bit build. The
3+
# limits of the 64-bit build are so large that they cannot be tested
4+
# on accessible hardware.
5+
#
6+
7+
import sys
8+
from decimal import *
9+
from gmpy import mpz
10+
11+
12+
_PyHASH_MODULUS = sys.hash_info.modulus
13+
# hash values to use for positive and negative infinities, and nans
14+
_PyHASH_INF = sys.hash_info.inf
15+
_PyHASH_NAN = sys.hash_info.nan
16+
17+
# _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS
18+
_PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS)
19+
20+
def xhash(coeff, exp):
21+
sign = 1
22+
if coeff < 0:
23+
sign = -1
24+
coeff = -coeff
25+
if exp >= 0:
26+
exp_hash = pow(10, exp, _PyHASH_MODULUS)
27+
else:
28+
exp_hash = pow(_PyHASH_10INV, -exp, _PyHASH_MODULUS)
29+
hash_ = coeff * exp_hash % _PyHASH_MODULUS
30+
ans = hash_ if sign == 1 else -hash_
31+
return -2 if ans == -1 else ans
32+
33+
34+
x = mpz(10) ** 425000000 - 1
35+
coeff = int(x)
36+
37+
d = Decimal('9' * 425000000 + 'e-849999999')
38+
39+
h1 = xhash(coeff, -849999999)
40+
h2 = hash(d)
41+
42+
assert h2 == h1
43+
44+
45+

0 commit comments

Comments
 (0)