|
14 | 14 |
|
15 | 15 | import re |
16 | 16 | import decimal |
| 17 | +try: |
| 18 | + import _decimal |
| 19 | +except ImportError: |
| 20 | + _decimal = None |
17 | 21 |
|
18 | 22 |
|
19 | 23 | def int_to_decimal(n): |
@@ -82,7 +86,47 @@ def inner(n, w): |
82 | 86 |
|
83 | 87 | def int_to_decimal_string(n): |
84 | 88 | """Asymptotically fast conversion of an 'int' to a decimal string.""" |
85 | | - return str(int_to_decimal(n)) |
| 89 | + w = n.bit_length() |
| 90 | + if w > 450_000 and _decimal is not None: |
| 91 | + # It is only usable with the C decimal implementation. |
| 92 | + # _pydecimal.py calls str() on very large integers, which in its |
| 93 | + # turn calls int_to_decimal_string(), causing very deep recursion. |
| 94 | + return str(int_to_decimal(n)) |
| 95 | + |
| 96 | + # Fallback algorithm for the case when the C decimal module isn't |
| 97 | + # available. This algorithm is asymptotically worse than the algorithm |
| 98 | + # using the decimal module, but better than the quadratic time |
| 99 | + # implementation in longobject.c. |
| 100 | + def inner(n, w): |
| 101 | + if w <= 1000: |
| 102 | + return str(n) |
| 103 | + w2 = w >> 1 |
| 104 | + d = pow10_cache.get(w2) |
| 105 | + if d is None: |
| 106 | + d = pow10_cache[w2] = 5**w2 << w2 # 10**i = (5*2)**i = 5**i * 2**i |
| 107 | + hi, lo = divmod(n, d) |
| 108 | + return inner(hi, w - w2) + inner(lo, w2).zfill(w2) |
| 109 | + |
| 110 | + # The estimation of the number of decimal digits. |
| 111 | + # There is no harm in small error. If we guess too large, there may |
| 112 | + # be leading 0's that need to be stripped. If we guess too small, we |
| 113 | + # may need to call str() recursively for the remaining highest digits, |
| 114 | + # which can still potentially be a large integer. This is manifested |
| 115 | + # only if the number has way more than 10**15 digits, that exceeds |
| 116 | + # the 52-bit physical address limit in both Intel64 and AMD64. |
| 117 | + w = int(w * 0.3010299956639812 + 1) # log10(2) |
| 118 | + pow10_cache = {} |
| 119 | + if n < 0: |
| 120 | + n = -n |
| 121 | + sign = '-' |
| 122 | + else: |
| 123 | + sign = '' |
| 124 | + s = inner(n, w) |
| 125 | + if s[0] == '0' and n: |
| 126 | + # If our guess of w is too large, there may be leading 0's that |
| 127 | + # need to be stripped. |
| 128 | + s = s.lstrip('0') |
| 129 | + return sign + s |
86 | 130 |
|
87 | 131 |
|
88 | 132 | def _str_to_int_inner(s): |
|
0 commit comments