2019-11-21 13:57:40 +01:00
|
|
|
|
#!/usr/bin/env python3
|
2015-07-26 15:44:24 +02:00
|
|
|
|
|
|
|
|
|
"""
|
2021-07-17 00:30:34 -05:00
|
|
|
|
Generate powers of five using Daniel Lemire's ``Eisel-Lemire algorithm`` for use in
|
2015-07-26 15:44:24 +02:00
|
|
|
|
decimal to floating point conversions.
|
|
|
|
|
|
|
|
|
|
Specifically, computes and outputs (as Rust code) a table of 10^e for some
|
2021-07-17 00:30:34 -05:00
|
|
|
|
range of exponents e. The output is one array of 128 bit significands.
|
|
|
|
|
The base two exponents can be inferred using a logarithmic slope
|
|
|
|
|
of the decimal exponent. The approximations are normalized and rounded perfectly,
|
|
|
|
|
i.e., within 0.5 ULP of the true value.
|
2015-07-26 15:44:24 +02:00
|
|
|
|
|
2021-07-17 00:30:34 -05:00
|
|
|
|
Adapted from Daniel Lemire's fast_float ``table_generation.py``,
|
|
|
|
|
available here: <https://github.com/fastfloat/fast_float/blob/main/script/table_generation.py>.
|
2015-07-26 15:44:24 +02:00
|
|
|
|
"""
|
|
|
|
|
from __future__ import print_function
|
2021-07-17 00:30:34 -05:00
|
|
|
|
from math import ceil, floor, log, log2
|
2015-07-26 15:44:24 +02:00
|
|
|
|
from fractions import Fraction
|
2021-07-17 00:30:34 -05:00
|
|
|
|
from collections import deque
|
2020-02-07 23:49:40 -05:00
|
|
|
|
|
2015-12-30 14:01:42 +01:00
|
|
|
|
HEADER = """
|
2021-07-17 00:30:34 -05:00
|
|
|
|
//! Pre-computed tables powers-of-5 for extended-precision representations.
|
|
|
|
|
//!
|
|
|
|
|
//! These tables enable fast scaling of the significant digits
|
|
|
|
|
//! of a float to the decimal exponent, with minimal rounding
|
|
|
|
|
//! errors, in a 128 or 192-bit representation.
|
|
|
|
|
//!
|
2015-12-30 14:01:42 +01:00
|
|
|
|
//! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py`
|
2015-07-26 15:44:24 +02:00
|
|
|
|
"""
|
|
|
|
|
|
2021-07-17 00:30:34 -05:00
|
|
|
|
STATIC_WARNING = """
|
|
|
|
|
// Use static to avoid long compile times: Rust compiler errors
|
|
|
|
|
// can have the entire table compiled multiple times, and then
|
|
|
|
|
// emit code multiple times, even if it's stripped out in
|
|
|
|
|
// the final binary.
|
|
|
|
|
"""
|
2015-12-30 14:01:42 +01:00
|
|
|
|
|
2015-07-26 15:44:24 +02:00
|
|
|
|
def main():
|
2021-07-17 00:30:34 -05:00
|
|
|
|
min_exp = minimum_exponent(10)
|
|
|
|
|
max_exp = maximum_exponent(10)
|
|
|
|
|
bias = -minimum_exponent(5)
|
|
|
|
|
|
2015-12-30 14:01:42 +01:00
|
|
|
|
print(HEADER.strip())
|
|
|
|
|
print()
|
2021-07-17 00:30:34 -05:00
|
|
|
|
print('pub const SMALLEST_POWER_OF_FIVE: i32 = {};'.format(min_exp))
|
|
|
|
|
print('pub const LARGEST_POWER_OF_FIVE: i32 = {};'.format(max_exp))
|
|
|
|
|
print('pub const N_POWERS_OF_FIVE: usize = ', end='')
|
|
|
|
|
print('(LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;')
|
2015-12-30 14:01:42 +01:00
|
|
|
|
print()
|
2021-07-17 00:30:34 -05:00
|
|
|
|
print_proper_powers(min_exp, max_exp, bias)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def minimum_exponent(base):
|
|
|
|
|
return ceil(log(5e-324, base) - log(0xFFFFFFFFFFFFFFFF, base))
|
|
|
|
|
|
2015-12-30 14:01:42 +01:00
|
|
|
|
|
2021-07-17 00:30:34 -05:00
|
|
|
|
def maximum_exponent(base):
|
|
|
|
|
return floor(log(1.7976931348623157e+308, base))
|
2015-12-30 14:01:42 +01:00
|
|
|
|
|
2021-07-17 00:30:34 -05:00
|
|
|
|
|
|
|
|
|
def print_proper_powers(min_exp, max_exp, bias):
|
|
|
|
|
powers = deque()
|
|
|
|
|
|
|
|
|
|
# Add negative exponents.
|
|
|
|
|
# 2^(2b)/(5^−q) with b=64 + int(math.ceil(log2(5^−q)))
|
2015-07-26 15:44:24 +02:00
|
|
|
|
powers = []
|
2021-07-17 00:30:34 -05:00
|
|
|
|
for q in range(min_exp, 0):
|
|
|
|
|
power5 = 5 ** -q
|
|
|
|
|
z = 0
|
|
|
|
|
while (1 << z) < power5:
|
|
|
|
|
z += 1
|
|
|
|
|
if q >= -27:
|
|
|
|
|
b = z + 127
|
|
|
|
|
c = 2 ** b // power5 + 1
|
|
|
|
|
powers.append((c, q))
|
|
|
|
|
else:
|
|
|
|
|
b = 2 * z + 2 * 64
|
|
|
|
|
c = 2 ** b // power5 + 1
|
|
|
|
|
# truncate
|
|
|
|
|
while c >= (1<<128):
|
|
|
|
|
c //= 2
|
|
|
|
|
powers.append((c, q))
|
|
|
|
|
|
|
|
|
|
# Add positive exponents
|
|
|
|
|
for q in range(0, max_exp + 1):
|
|
|
|
|
power5 = 5 ** q
|
|
|
|
|
# move the most significant bit in position
|
|
|
|
|
while power5 < (1<<127):
|
|
|
|
|
power5 *= 2
|
|
|
|
|
# *truncate*
|
|
|
|
|
while power5 >= (1<<128):
|
|
|
|
|
power5 //= 2
|
|
|
|
|
powers.append((power5, q))
|
|
|
|
|
|
|
|
|
|
# Print the powers.
|
|
|
|
|
print(STATIC_WARNING.strip())
|
|
|
|
|
print('#[rustfmt::skip]')
|
|
|
|
|
typ = '[(u64, u64); N_POWERS_OF_FIVE]'
|
|
|
|
|
print('pub static POWER_OF_FIVE_128: {} = ['.format(typ))
|
|
|
|
|
lo_mask = (1 << 64) - 1
|
|
|
|
|
for c, exp in powers:
|
|
|
|
|
hi = '0x{:x}'.format(c // (1 << 64))
|
|
|
|
|
lo = '0x{:x}'.format(c % (1 << 64))
|
|
|
|
|
value = ' ({}, {}), '.format(hi, lo)
|
|
|
|
|
comment = '// {}^{}'.format(5, exp)
|
|
|
|
|
print(value.ljust(46, ' ') + comment)
|
|
|
|
|
print('];')
|
2015-12-30 14:01:42 +01:00
|
|
|
|
|
|
|
|
|
|
2015-07-26 15:44:24 +02:00
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|