#!/usr/bin/env python3 """ Generate powers of five using Daniel Lemire's ``Eisel-Lemire algorithm`` for use in decimal to floating point conversions. Specifically, computes and outputs (as Rust code) a table of 10^e for some 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. Adapted from Daniel Lemire's fast_float ``table_generation.py``, available here: . """ from __future__ import print_function from math import ceil, floor, log from collections import deque HEADER = """ //! 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. //! //! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py` """ 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. """ def main(): min_exp = minimum_exponent(10) max_exp = maximum_exponent(10) bias = -minimum_exponent(5) print(HEADER.strip()) print() print("pub(super) const SMALLEST_POWER_OF_FIVE: i32 = {};".format(min_exp)) print("pub(super) const LARGEST_POWER_OF_FIVE: i32 = {};".format(max_exp)) print("pub(super) const N_POWERS_OF_FIVE: usize =") print(" (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;") print() print_proper_powers(min_exp, max_exp, bias) def minimum_exponent(base): return ceil(log(5e-324, base) - log(0xFFFFFFFFFFFFFFFF, base)) def maximum_exponent(base): return floor(log(1.7976931348623157e308, base)) 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))) powers = [] 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(super) static POWER_OF_FIVE_128: {} = [".format(typ)) 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("];") if __name__ == "__main__": main()