summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/_pydecimal.py19
-rw-r--r--Lib/test/test_decimal.py25
-rw-r--r--Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst1
3 files changed, 40 insertions, 5 deletions
diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py
index de4561a..613123e 100644
--- a/Lib/_pydecimal.py
+++ b/Lib/_pydecimal.py
@@ -2131,10 +2131,16 @@ class Decimal(object):
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:
@@ -2184,13 +2190,18 @@ class Decimal(object):
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))
diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index 7010c34..e927e24 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -4716,9 +4716,33 @@ class PyWhitebox(unittest.TestCase):
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
@@ -5705,7 +5729,6 @@ class CWhitebox(unittest.TestCase):
with C.localcontext(rounding=C.ROUND_DOWN):
self.assertEqual(format(y, '#.1f'), '6.0')
-
@requires_docstrings
@requires_cdecimal
class SignatureTest(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst b/Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst
new file mode 100644
index 0000000..80dc868
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-05-04-20-22-59.gh-issue-118164.9D02MQ.rst
@@ -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).