rust/src/etc/dec2flt_table.py
Alex Huszagh 8752b40369 Changed dec2flt to use the Eisel-Lemire algorithm.
Implementation is based off fast-float-rust, with a few notable changes.

- Some unsafe methods have been removed.
- Safe methods with inherently unsafe functionality have been removed.
- All unsafe functionality is documented and provably safe.
- Extensive documentation has been added for simpler maintenance.
- Inline annotations on internal routines has been removed.
- Fixed Python errors in src/etc/test-float-parse/runtests.py.
- Updated test-float-parse to be a library, to avoid missing rand dependency.
- Added regression tests for #31109 and #31407 in core tests.
- Added regression tests for #31109 and #31407 in ui tests.
- Use the existing slice primitive to simplify shared dec2flt methods
- Remove Miri ignores from dec2flt, due to faster parsing times.

- resolves #85198
- resolves #85214
- resolves #85234
- fixes #31407
- fixes #31109
- fixes #53015
- resolves #68396
- closes https://github.com/aldanor/fast-float-rust/issues/15
2021-07-17 00:30:34 -05:00

112 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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: <https://github.com/fastfloat/fast_float/blob/main/script/table_generation.py>.
"""
from __future__ import print_function
from math import ceil, floor, log, log2
from fractions import Fraction
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 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;')
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.7976931348623157e+308, 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 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('];')
if __name__ == '__main__':
main()