@@ -447,6 +447,95 @@ def IEEEContext(bits, /):
447447
448448##### Decimal class #######################################################
449449
450+ # Observation: For all q >= 0 and a >= 1, q < 10**a iff len(str(q)) <= a.
451+ #
452+ # The constants below are used to speed-up "q < 10 ** a" checks to avoid
453+ # computing len(str(q)) as much as possible. Those speed-ups are based on
454+ # the following claims.
455+ #
456+ # See https://github.com/python/cpython/issues/140036 for details.
457+
458+ _LOG_10_BASE_2_LO = float .fromhex ('0x1.a934f0979a371p+1' )
459+ assert pow (2 , _LOG_10_BASE_2_LO ) < 10
460+
461+ _LOG_10_BASE_2_HI = float .fromhex ('0x1.a934f0979a372p+1' )
462+ assert pow (2 , _LOG_10_BASE_2_HI ) > 10
463+
464+
465+ def _tento (n ):
466+ """Compute 10 ** n with 1 base-5 exponentiation and 1 bit-shift."""
467+ return (5 ** n ) << n
468+
469+
470+ def _is_less_than_pow10a_use_str (q , a ):
471+ """Try to efficiently check len(str(q)) <= a, or equivalently q < 10**a.
472+
473+ If the comparison cannot be obtained from q.bit_length(),
474+ then str(q) is explicitly computed and may raise ValueError.
475+
476+ Return (len(str(q)) <= a, None) or (len(str(q)) <= a, str(q)).
477+ """
478+ if q .bit_length () < a * _LOG_10_BASE_2_LO :
479+ # Claim: If 0 < z <= log2(10) and q.bit_length() < a*z, then q < 10**a.
480+ # Proof: By contradiction, q >= 10**a. By definition,
481+ # log2(q) >= a*log2(10) >= a*z > q.bit_length().
482+ # In particular, q > 2**q.bit_length(), which is impossible.
483+
484+ # assert q < 10 ** a
485+ return True , None
486+ elif q .bit_length () >= 1 + a * _LOG_10_BASE_2_HI :
487+ # Claim: If z > log2(10) and q.bit_length() >= 1 + a*z, then q > 10**a.
488+ # Proof: Since q >= 2**(q.bit_length()-1), we have
489+ # q >= 2**(q.bit_length()-1) >= 2**(a*z) > 2**(a*log2(10)) = 10**a.
490+
491+ # assert q > 10 ** a
492+ return False , None
493+ # Handle cases that fail due to floating point precision loss
494+ # when computing _LOG_10_BASE_2_LO and _LOG_10_BASE_2_HI, or
495+ # that cannot be distinguished with (q.bit_length(), a) only.
496+ #
497+ # For instance, (q1, a) = (95, 2) and (q2, a) = (105, 2) produce
498+ # different results but q1.bit_length() == q2.bit_length() == 7.
499+ str_q = str (q ) # can raise a ValueError
500+ is_valid = len (str_q ) <= a
501+ return is_valid , str_q
502+
503+
504+ def _is_less_than_pow10a (q , a , * , exact = True , ulp_order = 20 ):
505+ """Check that len(str(q)) <= a without computing str(q).
506+
507+ When *exact* is false, computing len(str(q)) is replaced by f(q):
508+
509+ f(q) = floor(log10(q) + ulp(log10(q)) * ulp_order + 1.0)
510+
511+ Most of the time, f(q) = len(str(q)) but in some cases, it may
512+ happen that f(q) > len(str(q)).
513+
514+ When *exact* is true, computing len(str(q)) requires one bigint
515+ exponentiation that only depends on q.
516+ """
517+ if q < 10 :
518+ return a >= 1
519+
520+ z = _math .log10 (q )
521+ t = _math .ulp (z ) * ulp_order
522+
523+ if exact :
524+ intlo = int (z - t )
525+ inthi = int (z + t )
526+ diff = inthi - intlo
527+ assert diff in (0 , 1 )
528+ if diff == 1 :
529+ lo = _tento (inthi ) # may be slow
530+ if q < lo :
531+ inthi -= 1
532+ assert q >= (lo // 10 )
533+ ndigits = inthi + 1
534+ else :
535+ ndigits = int (z + t + 1.0 )
536+ return ndigits <= a
537+
538+
450539# Do not subclass Decimal from numbers.Real and do not register it as such
451540# (because Decimals are not interoperable with floats). See the notes in
452541# numbers.py for more detail.
@@ -1360,8 +1449,13 @@ def _divide(self, other, context):
13601449 else :
13611450 op2 .int *= 10 ** (op2 .exp - op1 .exp )
13621451 q , r = divmod (op1 .int , op2 .int )
1363- if q < 10 ** context .prec :
1364- return (_dec_from_triple (sign , str (q ), 0 ),
1452+ is_valid , str_q = _is_less_than_pow10a_use_str (q , context .prec )
1453+ if is_valid :
1454+ if str_q is None :
1455+ str_q = str (q )
1456+ # assert q < 10 ** context.prec
1457+ # assert len(str(q)) <= context.prec
1458+ return (_dec_from_triple (sign , str_q , 0 ),
13651459 _dec_from_triple (self ._sign , str (r ), ideal_exp ))
13661460
13671461 # Here the quotient is too large to be representable
@@ -1516,7 +1610,9 @@ def remainder_near(self, other, context=None):
15161610 r -= op2 .int
15171611 q += 1
15181612
1519- if q >= 10 ** context .prec :
1613+ if not _is_less_than_pow10a (q , context .prec ):
1614+ # assert q >= 10 ** context.prec
1615+ # assert len(str(q)) > context.prec
15201616 return context ._raise_error (DivisionImpossible )
15211617
15221618 # result has same sign as self unless r is negative
0 commit comments