Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-130599: precompute conversion constants for long() #130714

Merged
merged 9 commits into from
Mar 4, 2025
76 changes: 54 additions & 22 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2820,6 +2820,59 @@
just 1 digit at the start, so that the copying code was exercised for every
digit beyond the first.
***/

// Tables are computed by Tools/scripts/long_conv_tables.py
#if PYLONG_BITS_IN_DIGIT == 15
static double log_base_BASE[37] = {0.0, 0.0, 0.06666666666666667,
0.10566416671474375, 0.13333333333333333, 0.15479520632582416,
0.17233083338141042, 0.18715699480384027, 0.19999999999999998,
0.2113283334294875, 0.22146187299249084, 0.23062877457581984,
0.2389975000480771, 0.24669598120940617, 0.25382366147050694,
0.26045937304056793, 0.26666666666666666, 0.27249752275002265,
0.27799500009615413, 0.2831951675629057, 0.28812853965915747,
0.29282116151858406, 0.2972954412424865, 0.3015707970704675,
0.3056641667147438, 0.30959041265164833, 0.3133626478760728,
0.31699250014423125, 0.3204903281371736, 0.3238653996751715,
0.3271260397072346, 0.3302797540257917, 0.33333333333333337,
0.3362929412905636, 0.3391641894166893, 0.34195220112966446,
0.34466166676282084};
static int convmultmax_base[37] = {0, 0, 32768, 19683, 16384,
15625, 7776, 16807, 32768, 6561, 10000, 14641, 20736, 28561,
2744, 3375, 4096, 4913, 5832, 6859, 8000, 9261, 10648, 12167,
13824, 15625, 17576, 19683, 21952, 24389, 27000, 29791, 32768,
1089, 1156, 1225, 1296};
static twodigits convwidth_base[37] = {0, 0, 15, 9, 7, 6, 5, 5, 5,
4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 2, 2, 2, 2};
#elif PYLONG_BITS_IN_DIGIT == 30
static double log_base_BASE[37] = {0.0, 0.0, 0.03333333333333333,
0.05283208335737188, 0.06666666666666667, 0.07739760316291208,
0.08616541669070521, 0.09357849740192013, 0.09999999999999999,
0.10566416671474375, 0.11073093649624542, 0.11531438728790992,
0.11949875002403855, 0.12334799060470308, 0.12691183073525347,
0.13022968652028397, 0.13333333333333333, 0.13624876137501132,
0.13899750004807707, 0.14159758378145285, 0.14406426982957873,
0.14641058075929203, 0.14864772062124326, 0.15078539853523376,
0.1528320833573719, 0.15479520632582416, 0.1566813239380364,
0.15849625007211562, 0.1602451640685868, 0.16193269983758574,
0.1635630198536173, 0.16513987701289584, 0.16666666666666669,
0.1681464706452818, 0.16958209470834465, 0.17097610056483223,
0.17233083338141042};
static int convmultmax_base[37] = {0, 0, 1073741824, 387420489,
1073741824, 244140625, 362797056, 282475249, 1073741824,
387420489, 1000000000, 214358881, 429981696, 815730721,
105413504, 170859375, 268435456, 410338673, 612220032,
893871739, 64000000, 85766121, 113379904, 148035889,
191102976, 244140625, 308915776, 387420489, 481890304,
594823321, 729000000, 887503681, 1073741824, 39135393,
45435424, 52521875, 60466176};
static twodigits convwidth_base[37] = {0, 0, 30, 18, 15, 12, 11,
10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 5, 5, 5, 5};
#else
#error "invalid PYLONG_BITS_IN_DIGIT value"
#endif

static int
long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, int base, PyLongObject **res)
{
Expand All @@ -2832,28 +2885,7 @@
PyLongObject *z;
const char *p;

static double log_base_BASE[37] = {0.0e0,};
static int convwidth_base[37] = {0,};
static twodigits convmultmax_base[37] = {0,};

if (log_base_BASE[base] == 0.0) {
twodigits convmax = base;
int i = 1;

log_base_BASE[base] = (log((double)base) /
log((double)PyLong_BASE));
for (;;) {
twodigits next = convmax * base;
if (next > PyLong_BASE) {
break;
}
convmax = next;
++i;
}
convmultmax_base[base] = convmax;
assert(i > 0);
convwidth_base[base] = i;
}
assert(log_base_BASE[base] != 0.0);

/* Create an int object that can contain the largest possible
* integer with this base and length. Note that there's no
Expand Down Expand Up @@ -2882,7 +2914,7 @@
/* `convwidth` consecutive input digits are treated as a single
* digit in base `convmultmax`.
*/
convwidth = convwidth_base[base];

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build (arm64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build (arm64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build and test (x64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 2917 in Objects/longobject.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build and test (x64)

'=': conversion from 'twodigits' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]
convmultmax = convmultmax_base[base];

/* Work ;-) */
Expand Down
8 changes: 5 additions & 3 deletions Tools/c-analyzer/cpython/ignored.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ Python/thread_pthread.h PyThread__init_thread lib_initialized -
##-----------------------
## other values (not Python-specific)

# static tables computed by external script
Objects/longobject.c - log_base_BASE -
Objects/longobject.c - convwidth_base -
Objects/longobject.c - convmultmax_base -

## cached computed data - set lazily (*after* first init)
# XXX Are these safe relative to write races?
Objects/longobject.c long_from_non_binary_base log_base_BASE -
Objects/longobject.c long_from_non_binary_base convwidth_base -
Objects/longobject.c long_from_non_binary_base convmultmax_base -
Objects/unicodeobject.c - bloom_linebreak -
# This is safe:
Objects/unicodeobject.c _init_global_state initialized -
Expand Down
69 changes: 69 additions & 0 deletions Tools/scripts/long_conv_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
#
# Compute tables for longobject.c integer conversion.

import math
import textwrap


def format_array(name, values):
values = [str(v) for v in values]
values = ', '.join(values)
result = f'{name} = {{{values}}};'
result = textwrap.wrap(
result,
initial_indent=' ' * 4,
subsequent_indent=' ' * 8,
)
return '\n'.join(result)


def conv_tables(long_bits):
steps = 0
PyLong_BASE = 1 << long_bits
log_base_BASE = [0.0] * 37
convmultmax_base = [0] * 37
convwidth_base = [0] * 37
for base in range(2, 37):
convmax = base
i = 1
log_base_BASE[base] = math.log(float(base)) / math.log(PyLong_BASE)
while True:
steps += 1
next = convmax * base
if next > PyLong_BASE:
break
convmax = next
i += 1
convmultmax_base[base] = convmax
assert i > 0
convwidth_base[base] = i
# print('steps', steps)
return '\n'.join(
[
format_array('static double log_base_BASE[37]', log_base_BASE),
format_array('static int convmultmax_base[37]', convmultmax_base),
format_array(
'static twodigits convwidth_base[37]', convwidth_base
),
]
)


def main():
print(
f'''\
// Tables are computed by Tools/scripts/long_conv_tables.py
#if PYLONG_BITS_IN_DIGIT == 15
{conv_tables(15)}
#elif PYLONG_BITS_IN_DIGIT == 30
{conv_tables(30)}
#else
#error "invalid PYLONG_BITS_IN_DIGIT value"
#endif
'''
)


if __name__ == '__main__':
main()
Loading