Skip to content

Commit

Permalink
pythongh-118164: str(10**10000) hangs if the C _decimal module is mis…
Browse files Browse the repository at this point in the history
…sing (python#118503)

* Initial stab.

* Test the tentative fix. Hangs "forever" without this change.

* Move the new test to a better spot.

* New comment to explain why _convert_to_str allows any poewr of 10.

* Fixed a comment, and fleshed out an existing test that appeared unfinished.

* Added temporary asserts. Or maybe permanent ;-)

* Update Lib/_pydecimal.py

Co-authored-by: Serhiy Storchaka <[email protected]>

* Remove the new _convert_to_str().

Serhiy and I independently concluded that exact powers of 10
aren't possible in these contexts, so just checking the
string length is sufficient.

* At least for now, add the asserts to the other block too.

* 📜🤖 Added by blurb_it.

---------

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored May 4, 2024
1 parent 08d169f commit 999f0c5
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 5 deletions.
19 changes: 15 additions & 4 deletions Lib/_pydecimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2131,10 +2131,16 @@ def _power_exact(self, other, p):
else:
return None

if xc >= 10**p:
# An exact power of 10 is representable, but can convert to a
# string of any length. But an exact power of 10 shouldn't be
# possible at this point.
assert xc > 1, self
assert xc % 10 != 0, self
strxc = str(xc)
if len(strxc) > p:
return None
xe = -e-xe
return _dec_from_triple(0, str(xc), xe)
return _dec_from_triple(0, strxc, xe)

# now y is positive; find m and n such that y = m/n
if ye >= 0:
Expand Down Expand Up @@ -2184,13 +2190,18 @@ def _power_exact(self, other, p):
return None
xc = xc**m
xe *= m
if xc > 10**p:
# An exact power of 10 is representable, but can convert to a string
# of any length. But an exact power of 10 shouldn't be possible at
# this point.
assert xc > 1, self
assert xc % 10 != 0, self
str_xc = str(xc)
if len(str_xc) > p:
return None

# by this point the result *is* exactly representable
# adjust the exponent to get as close as possible to the ideal
# exponent, if necessary
str_xc = str(xc)
if other._isinteger() and other._sign == 0:
ideal_exponent = self._exp*int(other)
zeros = min(xe-ideal_exponent, p-len(str_xc))
Expand Down
25 changes: 24 additions & 1 deletion Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4716,9 +4716,33 @@ def test_py_exact_power(self):

c.prec = 1
x = Decimal("152587890625") ** Decimal('-0.5')
self.assertEqual(x, Decimal('3e-6'))
c.prec = 2
x = Decimal("152587890625") ** Decimal('-0.5')
self.assertEqual(x, Decimal('2.6e-6'))
c.prec = 3
x = Decimal("152587890625") ** Decimal('-0.5')
self.assertEqual(x, Decimal('2.56e-6'))
c.prec = 28
x = Decimal("152587890625") ** Decimal('-0.5')
self.assertEqual(x, Decimal('2.56e-6'))

c.prec = 201
x = Decimal(2**578) ** Decimal("-0.5")

# See https://github.com/python/cpython/issues/118027
# Testing for an exact power could appear to hang, in the Python
# version, as it attempted to compute 10**(MAX_EMAX + 1).
# Fixed via https://github.com/python/cpython/pull/118503.
c.prec = P.MAX_PREC
c.Emax = P.MAX_EMAX
c.Emin = P.MIN_EMIN
c.traps[P.Inexact] = 1
D2 = Decimal(2)
# If the bug is still present, the next statement won't complete.
res = D2 ** 117
self.assertEqual(res, 1 << 117)

def test_py_immutability_operations(self):
# Do operations and check that it didn't change internal objects.
Decimal = P.Decimal
Expand Down Expand Up @@ -5705,7 +5729,6 @@ def test_format_fallback_rounding(self):
with C.localcontext(rounding=C.ROUND_DOWN):
self.assertEqual(format(y, '#.1f'), '6.0')


@requires_docstrings
@requires_cdecimal
class SignatureTest(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Python implementation of the ``decimal`` module could appear to hang in relatively small power cases (like ``2**117``) if context precision was set to a very high value. A different method to check for exactly representable results is used now that doesn't rely on computing ``10**precision`` (which could be effectively too large to compute).

0 comments on commit 999f0c5

Please sign in to comment.