@@ -450,19 +450,84 @@ def IEEEContext(bits, /):
450450#
451451# See https://github.com/python/cpython/issues/140036 for details.
452452
453- # Claim: If 0 < z <= log2(10) and q.bit_length() < a*z, then q < 10**a.
454- # Proof: By contradiction, q >= 10**a. By definition,
455- # log2(q) >= a*log2(10) >= a*z > q.bit_length().
456- # In particular, q > 2**q.bit_length(), which is impossible.
457453_LOG_10_BASE_2_LO = float .fromhex ('0x1.a934f0979a371p+1' )
458454assert pow (2 , _LOG_10_BASE_2_LO ) < 10
459455
460- # Claim: If z > log2(10) and q.bit_length() >= 1 + a*z, then q > 10**a.
461- # Proof: Since q >= 2**(q.bit_length()-1), we have
462- # q >= 2**(q.bit_length()-1) >= 2**(a*z) > 2**(a*log2(10)) = 10**a.
463456_LOG_10_BASE_2_HI = float .fromhex ('0x1.a934f0979a372p+1' )
464457assert pow (2 , _LOG_10_BASE_2_HI ) > 10
465458
459+
460+ def _tento (n ):
461+ """Compute 10 ** n with 1 base-5 exponentiation and 1 bit-shift."""
462+ return (5 ** n ) << n
463+
464+
465+ def _is_leq_than_pow10a_use_str (q , a ):
466+ """Try to efficiently check len(str(q)) <= a, or equivalently q < 10**a.
467+
468+ If it is not possible to efficiently compute len(str(q)),
469+ this explicitly compute str(q) instead.
470+
471+ Return (len(str(q)) <= a, None) or (len(str(q)) <= a, str(q)).
472+ """
473+ if q .bit_length () < a * _LOG_10_BASE_2_LO :
474+ # Claim: If 0 < z <= log2(10) and q.bit_length() < a*z, then q < 10**a.
475+ # Proof: By contradiction, q >= 10**a. By definition,
476+ # log2(q) >= a*log2(10) >= a*z > q.bit_length().
477+ # In particular, q > 2**q.bit_length(), which is impossible.
478+
479+ # assert q < 10 ** context.prec
480+ return True , None
481+ elif q .bit_length () >= 1 + a * _LOG_10_BASE_2_HI :
482+ # Claim: If z > log2(10) and q.bit_length() >= 1 + a*z, then q > 10**a.
483+ # Proof: Since q >= 2**(q.bit_length()-1), we have
484+ # q >= 2**(q.bit_length()-1) >= 2**(a*z) > 2**(a*log2(10)) = 10**a.
485+
486+ # assert q > 10 ** context.prec
487+ return False , None
488+ # Handles other cases due to floating point precision loss
489+ # when computing _LOG_10_BASE_2_LO and _LOG_10_BASE_2_HI.
490+ str_q = str (q ) # can raise a ValueError
491+ is_valid = len (str_q ) <= a
492+ return is_valid , str_q
493+
494+
495+ def _is_leq_than_pow10a (q , a , * , exact = True , ulp_order = 20 ):
496+ """Check that len(str(q)) <= a without computing str(q).
497+
498+ When *exact* is false, computing len(str(q)) is replaced by f(q):
499+
500+ f(q) = floor(log10(q) + ulp(log10(q)) * ulp_order + 1.0)
501+
502+ Most of the time, f(q) = len(str(q)) but in some cases, it may
503+ happen that f(q) > len(str(q)).
504+
505+ When *exact* is true, computing len(str(q)) requires one bigint
506+ exponentiation that only depends on q.
507+ """
508+
509+ if q < 10 :
510+ return a >= 1
511+
512+ z = _math .log10 (q )
513+ t = _math .ulp (z ) * ulp_order
514+
515+ if exact :
516+ intlo = int (z - t )
517+ inthi = int (z + t )
518+ diff = inthi - intlo
519+ assert diff in (0 , 1 )
520+ if diff == 1 :
521+ lo = _tento (inthi ) # may be slow
522+ if q < lo :
523+ inthi -= 1
524+ assert q >= (lo // 10 )
525+ ndigits = inthi + 1
526+ else :
527+ ndigits = int (z + t + 1.0 )
528+ return ndigits <= a
529+
530+
466531# Do not subclass Decimal from numbers.Real and do not register it as such
467532# (because Decimals are not interoperable with floats). See the notes in
468533# numbers.py for more detail.
0 commit comments