1cbd624adSopenharmony_ci#!/usr/bin/env python3
2cbd624adSopenharmony_ci
3cbd624adSopenharmony_ci"""
4cbd624adSopenharmony_ciGenerate powers of a given radix for the Bellerophon algorithm.
5cbd624adSopenharmony_ci
6cbd624adSopenharmony_ciSpecifically, computes and outputs (as Rust code) a table of 10^e for some
7cbd624adSopenharmony_cirange of exponents e. The output is one array of 128 bit significands.
8cbd624adSopenharmony_ciThe base two exponents can be inferred using a logarithmic slope
9cbd624adSopenharmony_ciof the decimal exponent. The approximations are normalized and rounded perfectly,
10cbd624adSopenharmony_cii.e., within 0.5 ULP of the true value.
11cbd624adSopenharmony_ci
12cbd624adSopenharmony_ciPorted from Rust's core library implementation, which itself is
13cbd624adSopenharmony_ciadapted from Daniel Lemire's fast_float ``table_generation.py``,
14cbd624adSopenharmony_ciavailable here: <https://github.com/fastfloat/fast_float/blob/main/script/table_generation.py>.
15cbd624adSopenharmony_ci"""
16cbd624adSopenharmony_ci
17cbd624adSopenharmony_ciimport math
18cbd624adSopenharmony_cifrom collections import deque
19cbd624adSopenharmony_ci
20cbd624adSopenharmony_ciSTEP_STR = "static BASE{0}_STEP: i32 = {1};"
21cbd624adSopenharmony_ciSMALL_MANTISSA_STR = "static BASE{0}_SMALL_MANTISSA: [u64; {1}] = ["
22cbd624adSopenharmony_ciSMALL_EXPONENT_STR = "static BASE{0}_SMALL_EXPONENT: [i32; {1}] = ["
23cbd624adSopenharmony_ciLARGE_MANTISSA_STR = "static BASE{0}_LARGE_MANTISSA: [u64; {1}] = ["
24cbd624adSopenharmony_ciLARGE_EXPONENT_STR = "static BASE{0}_LARGE_EXPONENT: [i32; {1}] = ["
25cbd624adSopenharmony_ciSMALL_INT_STR = "static BASE{0}_SMALL_INT_POWERS: [u64; {1}] = {2};"
26cbd624adSopenharmony_ciBIAS_STR = "static BASE{0}_BIAS: i32 = {1};"
27cbd624adSopenharmony_ciEXP_STR = "// {}^{}"
28cbd624adSopenharmony_ciPOWER_STR = """pub static BASE{0}_POWERS: BellerophonPowers = BellerophonPowers {{
29cbd624adSopenharmony_ci    small: ExtendedFloatArray {{ mant: &BASE{0}_SMALL_MANTISSA, exp: &BASE{0}_SMALL_EXPONENT }},
30cbd624adSopenharmony_ci    large: ExtendedFloatArray {{ mant: &BASE{0}_LARGE_MANTISSA, exp: &BASE{0}_LARGE_EXPONENT }},
31cbd624adSopenharmony_ci    small_int: &BASE{0}_SMALL_INT_POWERS,
32cbd624adSopenharmony_ci    step: BASE{0}_STEP,
33cbd624adSopenharmony_ci    bias: BASE{0}_BIAS,
34cbd624adSopenharmony_ci}};\n"""
35cbd624adSopenharmony_ci
36cbd624adSopenharmony_cidef calculate_bitshift(base, exponent):
37cbd624adSopenharmony_ci    '''
38cbd624adSopenharmony_ci    Calculate the bitshift required for a given base. The exponent
39cbd624adSopenharmony_ci    is the absolute value of the max exponent (log distance from 1.)
40cbd624adSopenharmony_ci    '''
41cbd624adSopenharmony_ci
42cbd624adSopenharmony_ci    return 63 + math.ceil(math.log2(base**exponent))
43cbd624adSopenharmony_ci
44cbd624adSopenharmony_ci
45cbd624adSopenharmony_cidef next_fp(fp, base, step = 1):
46cbd624adSopenharmony_ci    '''Generate the next extended-floating point value.'''
47cbd624adSopenharmony_ci
48cbd624adSopenharmony_ci    return (fp[0] * (base**step), fp[1])
49cbd624adSopenharmony_ci
50cbd624adSopenharmony_ci
51cbd624adSopenharmony_cidef prev_fp(fp, base, step = 1):
52cbd624adSopenharmony_ci    '''Generate the previous extended-floating point value.'''
53cbd624adSopenharmony_ci
54cbd624adSopenharmony_ci    return (fp[0] // (base**step), fp[1])
55cbd624adSopenharmony_ci
56cbd624adSopenharmony_ci
57cbd624adSopenharmony_cidef normalize_fp(fp):
58cbd624adSopenharmony_ci    '''Normalize a extended-float so the MSB is the 64th bit'''
59cbd624adSopenharmony_ci
60cbd624adSopenharmony_ci    while fp[0] >> 64 != 0:
61cbd624adSopenharmony_ci        fp = (fp[0] >> 1, fp[1] + 1)
62cbd624adSopenharmony_ci    return fp
63cbd624adSopenharmony_ci
64cbd624adSopenharmony_ci
65cbd624adSopenharmony_cidef generate_small(base, count):
66cbd624adSopenharmony_ci    '''Generate the small powers for a given base'''
67cbd624adSopenharmony_ci
68cbd624adSopenharmony_ci    bitshift = calculate_bitshift(base, count)
69cbd624adSopenharmony_ci    fps = []
70cbd624adSopenharmony_ci    fp = (1 << bitshift, -bitshift)
71cbd624adSopenharmony_ci    for exp in range(count):
72cbd624adSopenharmony_ci        fps.append((normalize_fp(fp), exp))
73cbd624adSopenharmony_ci        fp = next_fp(fp, base)
74cbd624adSopenharmony_ci
75cbd624adSopenharmony_ci    # Print the small powers as integers.
76cbd624adSopenharmony_ci    ints = [base**i for _, i in fps]
77cbd624adSopenharmony_ci
78cbd624adSopenharmony_ci    return fps, ints
79cbd624adSopenharmony_ci
80cbd624adSopenharmony_ci
81cbd624adSopenharmony_cidef generate_large(base, step):
82cbd624adSopenharmony_ci    '''Generate the large powers for a given base.'''
83cbd624adSopenharmony_ci
84cbd624adSopenharmony_ci    # Get our starting parameters
85cbd624adSopenharmony_ci    min_exp = math.floor(math.log(5e-324, base) - math.log(0xFFFFFFFFFFFFFFFF, base))
86cbd624adSopenharmony_ci    max_exp = math.ceil(math.log(1.7976931348623157e+308, base))
87cbd624adSopenharmony_ci    bitshift = calculate_bitshift(base, abs(min_exp - step))
88cbd624adSopenharmony_ci    fps = deque()
89cbd624adSopenharmony_ci
90cbd624adSopenharmony_ci    # Add negative exponents
91cbd624adSopenharmony_ci    # We need to go below the minimum exponent, since we need
92cbd624adSopenharmony_ci    # all resulting exponents to be positive.
93cbd624adSopenharmony_ci    fp = (1 << bitshift, -bitshift)
94cbd624adSopenharmony_ci    for exp in range(-step, min_exp-step, -step):
95cbd624adSopenharmony_ci        fp = prev_fp(fp, base, step)
96cbd624adSopenharmony_ci        fps.appendleft((normalize_fp(fp), exp))
97cbd624adSopenharmony_ci
98cbd624adSopenharmony_ci    # Add positive exponents
99cbd624adSopenharmony_ci    fp = (1 << bitshift, -bitshift)
100cbd624adSopenharmony_ci    fps.append((normalize_fp(fp), 0))
101cbd624adSopenharmony_ci    for exp in range(step, max_exp, step):
102cbd624adSopenharmony_ci        fp = next_fp(fp, base, step)
103cbd624adSopenharmony_ci        fps.append((normalize_fp(fp), exp))
104cbd624adSopenharmony_ci
105cbd624adSopenharmony_ci    # Return the smallest exp, AKA, the bias
106cbd624adSopenharmony_ci    return fps, -fps[0][1]
107cbd624adSopenharmony_ci
108cbd624adSopenharmony_ci
109cbd624adSopenharmony_cidef print_array(base, string, fps, index):
110cbd624adSopenharmony_ci    '''Print an entire array'''
111cbd624adSopenharmony_ci
112cbd624adSopenharmony_ci    print(string.format(base, len(fps)))
113cbd624adSopenharmony_ci    for fp, exp in fps:
114cbd624adSopenharmony_ci        value = "    {},".format(fp[index])
115cbd624adSopenharmony_ci        exp = EXP_STR.format(base, exp)
116cbd624adSopenharmony_ci        print(value.ljust(30, " ") + exp)
117cbd624adSopenharmony_ci    print("];")
118cbd624adSopenharmony_ci
119cbd624adSopenharmony_ci
120cbd624adSopenharmony_cidef generate_base(base):
121cbd624adSopenharmony_ci    '''Generate all powers and variables.'''
122cbd624adSopenharmony_ci
123cbd624adSopenharmony_ci    step = math.floor(math.log(1e10, base))
124cbd624adSopenharmony_ci    small, ints = generate_small(base, step)
125cbd624adSopenharmony_ci    large, bias = generate_large(base, step)
126cbd624adSopenharmony_ci
127cbd624adSopenharmony_ci    print_array(base, SMALL_MANTISSA_STR, small, 0)
128cbd624adSopenharmony_ci    print_array(base, SMALL_EXPONENT_STR, small, 1)
129cbd624adSopenharmony_ci    print_array(base, LARGE_MANTISSA_STR, large, 0)
130cbd624adSopenharmony_ci    print_array(base, LARGE_EXPONENT_STR, large, 1)
131cbd624adSopenharmony_ci    print(SMALL_INT_STR.format(base, len(ints), ints))
132cbd624adSopenharmony_ci    print(STEP_STR.format(base, step))
133cbd624adSopenharmony_ci    print(BIAS_STR.format(base, bias))
134cbd624adSopenharmony_ci
135cbd624adSopenharmony_ci
136cbd624adSopenharmony_cidef generate():
137cbd624adSopenharmony_ci    '''Generate all bases.'''
138cbd624adSopenharmony_ci
139cbd624adSopenharmony_ci    print("// BASE{}\n".format(10))
140cbd624adSopenharmony_ci    generate_base(10)
141cbd624adSopenharmony_ci    print("")
142cbd624adSopenharmony_ci
143cbd624adSopenharmony_ci    print("// HIGH LEVEL\n// ----------\n")
144cbd624adSopenharmony_ci    print(POWER_STR.format(10))
145cbd624adSopenharmony_ci
146cbd624adSopenharmony_ci
147cbd624adSopenharmony_ciif __name__ == '__main__':
148cbd624adSopenharmony_ci    generate()
149