import os
LO, HI = -342, 308
def entry(q):
if q >= 0:
c = 5 ** q
n = c.bit_length()
b = c >> (n - 128) if n > 128 else c << (128 - n)
elif q >= -27:
c = 5 ** (-q)
n = c.bit_length()
b = ((1 << (127 + n)) // c) + 1
else:
c = 5 ** (-q)
n = c.bit_length()
b = ((1 << (2 * n + 128)) // c) + 1
while b >= (1 << 128):
b >>= 1
assert b >> 127 == 1 and b < (1 << 128), q
return b & 0xFFFFFFFFFFFFFFFF, b >> 64
def main():
assert entry(0) == (0, 0x8000000000000000)
assert entry(-1) == (0xCCCCCCCCCCCCCCCD, 0xCCCCCCCCCCCCCCCC)
assert entry(1) == (0, 0xA000000000000000)
rows = "\n".join(
" (0x{:016x}, 0x{:016x}),".format(*entry(q)) for q in range(LO, HI + 1)
)
src = f'''//! 128-bit truncated powers of five, for Eisel-Lemire float parsing.
//!
//! Entry `q` holds the 128 most significant bits of `5^q`, normalized so the
//! top bit is set. Every entry is truncated, never rounded up, so the stored
//! value never exceeds the true one. That fixed error direction is what lets
//! the parser detect the rare cases where 128 bits are not enough to decide
//! the rounding: it only has to watch for a carry out of the low word.
//!
//! The exception is `-27 <= q < 0`, where the reciprocal fits in 64 bits and
//! the rounded-up value is exact. That range sits inside the exponent window
//! the parser already treats as always-safe.
//!
//! Generated, not hand written. See `tools/gen_pow5.py`.
/// Lowest decimal exponent in [`POWER_OF_FIVE_128`].
pub const SMALLEST_POWER_OF_FIVE: i32 = {LO};
/// Highest decimal exponent in [`POWER_OF_FIVE_128`].
pub const LARGEST_POWER_OF_FIVE: i32 = {HI};
/// `(low 64 bits, high 64 bits)` of the normalized `5^q`, indexed by
/// `q - SMALLEST_POWER_OF_FIVE`.
pub static POWER_OF_FIVE_128: [(u64, u64); {HI - LO + 1}] = [
{rows}
];
'''
out = os.path.join(os.path.dirname(__file__), "..", "src", "num", "table.rs")
with open(out, "w") as f:
f.write(src)
print(f"wrote {os.path.normpath(out)}: {HI - LO + 1} entries")
if __name__ == "__main__":
main()