1cbd624adSopenharmony_ci#!/usr/bin/env python3
2cbd624adSopenharmony_ci
3cbd624adSopenharmony_ci"""
4cbd624adSopenharmony_ciGenerate powers of five using Daniel Lemire's ``Eisel-Lemire algorithm`` for use in
5cbd624adSopenharmony_cidecimal to floating point conversions.
6cbd624adSopenharmony_ci
7cbd624adSopenharmony_ciSpecifically, computes and outputs (as Rust code) a table of 10^e for some
8cbd624adSopenharmony_cirange of exponents e. The output is one array of 128 bit significands.
9cbd624adSopenharmony_ciThe base two exponents can be inferred using a logarithmic slope
10cbd624adSopenharmony_ciof the decimal exponent. The approximations are normalized and rounded perfectly,
11cbd624adSopenharmony_cii.e., within 0.5 ULP of the true value.
12cbd624adSopenharmony_ci
13cbd624adSopenharmony_ciPorted from Rust's core library implementation, which itself is
14cbd624adSopenharmony_ciadapted from Daniel Lemire's fast_float ``table_generation.py``,
15cbd624adSopenharmony_ciavailable here: <https://github.com/fastfloat/fast_float/blob/main/script/table_generation.py>.
16cbd624adSopenharmony_ci"""
17cbd624adSopenharmony_cifrom __future__ import print_function
18cbd624adSopenharmony_cifrom math import ceil, floor, log, log2
19cbd624adSopenharmony_cifrom fractions import Fraction
20cbd624adSopenharmony_cifrom collections import deque
21cbd624adSopenharmony_ci
22cbd624adSopenharmony_ciHEADER = """
23cbd624adSopenharmony_ci//! Pre-computed tables powers-of-5 for extended-precision representations.
24cbd624adSopenharmony_ci//!
25cbd624adSopenharmony_ci//! These tables enable fast scaling of the significant digits
26cbd624adSopenharmony_ci//! of a float to the decimal exponent, with minimal rounding
27cbd624adSopenharmony_ci//! errors, in a 128 or 192-bit representation.
28cbd624adSopenharmony_ci//!
29cbd624adSopenharmony_ci//! DO NOT MODIFY: Generated by `src/etc/lemire_table.py`
30cbd624adSopenharmony_ci"""
31cbd624adSopenharmony_ci
32cbd624adSopenharmony_ciSTATIC_WARNING = """
33cbd624adSopenharmony_ci// Use static to avoid long compile times: Rust compiler errors
34cbd624adSopenharmony_ci// can have the entire table compiled multiple times, and then
35cbd624adSopenharmony_ci// emit code multiple times, even if it's stripped out in
36cbd624adSopenharmony_ci// the final binary.
37cbd624adSopenharmony_ci"""
38cbd624adSopenharmony_ci
39cbd624adSopenharmony_cidef main():
40cbd624adSopenharmony_ci    min_exp = minimum_exponent(10)
41cbd624adSopenharmony_ci    max_exp = maximum_exponent(10)
42cbd624adSopenharmony_ci    bias = -minimum_exponent(5)
43cbd624adSopenharmony_ci
44cbd624adSopenharmony_ci    print(HEADER.strip())
45cbd624adSopenharmony_ci    print()
46cbd624adSopenharmony_ci    print('pub const SMALLEST_POWER_OF_FIVE: i32 = {};'.format(min_exp))
47cbd624adSopenharmony_ci    print('pub const LARGEST_POWER_OF_FIVE: i32 = {};'.format(max_exp))
48cbd624adSopenharmony_ci    print('pub const N_POWERS_OF_FIVE: usize = ', end='')
49cbd624adSopenharmony_ci    print('(LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;')
50cbd624adSopenharmony_ci    print()
51cbd624adSopenharmony_ci    print_proper_powers(min_exp, max_exp, bias)
52cbd624adSopenharmony_ci
53cbd624adSopenharmony_ci
54cbd624adSopenharmony_cidef minimum_exponent(base):
55cbd624adSopenharmony_ci    return ceil(log(5e-324, base) - log(0xFFFFFFFFFFFFFFFF, base))
56cbd624adSopenharmony_ci
57cbd624adSopenharmony_ci
58cbd624adSopenharmony_cidef maximum_exponent(base):
59cbd624adSopenharmony_ci    return floor(log(1.7976931348623157e+308, base))
60cbd624adSopenharmony_ci
61cbd624adSopenharmony_ci
62cbd624adSopenharmony_cidef print_proper_powers(min_exp, max_exp, bias):
63cbd624adSopenharmony_ci    powers = deque()
64cbd624adSopenharmony_ci
65cbd624adSopenharmony_ci    # Add negative exponents.
66cbd624adSopenharmony_ci    # 2^(2b)/(5^−q) with b=64 + int(math.ceil(log2(5^−q)))
67cbd624adSopenharmony_ci    powers = []
68cbd624adSopenharmony_ci    for q in range(min_exp, 0):
69cbd624adSopenharmony_ci        power5 = 5 ** -q
70cbd624adSopenharmony_ci        z = 0
71cbd624adSopenharmony_ci        while (1 << z) < power5:
72cbd624adSopenharmony_ci            z += 1
73cbd624adSopenharmony_ci        if q >= -27:
74cbd624adSopenharmony_ci            b = z + 127
75cbd624adSopenharmony_ci            c = 2 ** b // power5 + 1
76cbd624adSopenharmony_ci            powers.append((c, q))
77cbd624adSopenharmony_ci        else:
78cbd624adSopenharmony_ci            b = 2 * z + 2 * 64
79cbd624adSopenharmony_ci            c = 2 ** b // power5 + 1
80cbd624adSopenharmony_ci            # truncate
81cbd624adSopenharmony_ci            while c >= (1<<128):
82cbd624adSopenharmony_ci                c //= 2
83cbd624adSopenharmony_ci            powers.append((c, q))
84cbd624adSopenharmony_ci
85cbd624adSopenharmony_ci    # Add positive exponents
86cbd624adSopenharmony_ci    for q in range(0, max_exp + 1):
87cbd624adSopenharmony_ci        power5 = 5 ** q
88cbd624adSopenharmony_ci        # move the most significant bit in position
89cbd624adSopenharmony_ci        while power5 < (1<<127):
90cbd624adSopenharmony_ci            power5 *= 2
91cbd624adSopenharmony_ci        # *truncate*
92cbd624adSopenharmony_ci        while power5 >= (1<<128):
93cbd624adSopenharmony_ci            power5 //= 2
94cbd624adSopenharmony_ci        powers.append((power5, q))
95cbd624adSopenharmony_ci
96cbd624adSopenharmony_ci    # Print the powers.
97cbd624adSopenharmony_ci    print(STATIC_WARNING.strip())
98cbd624adSopenharmony_ci    print('#[rustfmt::skip]')
99cbd624adSopenharmony_ci    typ = '[(u64, u64); N_POWERS_OF_FIVE]'
100cbd624adSopenharmony_ci    print('pub static POWER_OF_FIVE_128: {} = ['.format(typ))
101cbd624adSopenharmony_ci    lo_mask = (1 << 64) - 1
102cbd624adSopenharmony_ci    for c, exp in powers:
103cbd624adSopenharmony_ci        hi = '0x{:x}'.format(c // (1 << 64))
104cbd624adSopenharmony_ci        lo = '0x{:x}'.format(c % (1 << 64))
105cbd624adSopenharmony_ci        value = '    ({}, {}), '.format(hi, lo)
106cbd624adSopenharmony_ci        comment = '// {}^{}'.format(5, exp)
107cbd624adSopenharmony_ci        print(value.ljust(46, ' ') + comment)
108cbd624adSopenharmony_ci    print('];')
109cbd624adSopenharmony_ci
110cbd624adSopenharmony_ci
111cbd624adSopenharmony_ciif __name__ == '__main__':
112cbd624adSopenharmony_ci    main()
113