Two major off-by-one errors are fixed in this patch. The first is in float_to_string.h with length_for_num, which wasn't accounting for the implicit leading bit when calculating the length of a number, causing a missing digit on 80 bit float max. The other off-by-one is the ryu_long_double_constants.h (a.k.a the Mega Table) not having any entries for the last POW10_OFFSET in POW10_SPLIT. This was also found on 80 bit float max. Finally, the integer calculation mode was using a slightly too short integer, again on 80 bit float max, not accounting for the mantissa width. All of these are fixed in this patch.
205 lines
6.5 KiB
Python
205 lines
6.5 KiB
Python
"""
|
|
//===-- Table Generator for Ryu Printf ------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
This file is used to generate the tables of values in
|
|
src/__support/ryu_constants.h and ryu_long_double constants.h. To use it, set
|
|
the constants at the top of the file to the values you want to use for the Ryu
|
|
algorithm, then run this file. It will output the appropriate tables to stdout,
|
|
so it's recommended to pipe stdout to a file. The following is a brief
|
|
explenation of each of the constants.
|
|
|
|
BLOCK_SIZE:
|
|
Default: 9
|
|
The number of digits that will be calculated together in a block.
|
|
Don't touch this unless you really know what you're doing.
|
|
|
|
CONSTANT:
|
|
Default: 120
|
|
Also known as c_0 and c_1 in the Ryu Printf paper and SHIFT_CONST in
|
|
float_to_string.h.
|
|
The table value is shifted left by this amount, and the final value is
|
|
shifted right by this amount. It effectively makes the table value a fixed
|
|
point number with the decimal point at the bit that is CONSTANT bits from
|
|
the right.
|
|
Higher values increase accuracy, but also require higher MID_INT_WIDTH
|
|
values to fit the result.
|
|
|
|
IDX_SIZE:
|
|
Default: 16
|
|
By increasing the MOD_SIZE slightly we can significantly decrease the number
|
|
of table entries we need.
|
|
We can divide the number of table entries by IDX_SIZE, and multiply MOD_SIZE
|
|
by 2^IDX_SIZE, and the math still works out.
|
|
This optimization isn't mentioned in the original Ryu Printf paper but it
|
|
saves a lot of space.
|
|
|
|
MID_INT_WIDTH:
|
|
Default: 192
|
|
This is the size of integer that the tables use. It's called mid int because
|
|
it's the integer used in the middle of the calculation. There are large ints
|
|
used to calculate the mid int table values, then those are used to calculate
|
|
the small int final values.
|
|
This must be divisible by 64 since each table entry is an array of 64 bit
|
|
integers.
|
|
If this is too small, then the results will get cut off. It should be at
|
|
least CONSTANT + IDX_SIZE + log_2(10^9) to be able to fit the table values.
|
|
|
|
MANT_WIDTH:
|
|
The width of the widest float mantissa that this table will work for.
|
|
This has a small effect on table size.
|
|
|
|
EXP_WIDTH:
|
|
The width of the widest float exponent that this table will work for.
|
|
This has a large effect on table size.
|
|
Specifically, table size is proportional to the square of this number.
|
|
"""
|
|
|
|
BLOCK_SIZE = 9
|
|
|
|
|
|
# Values for double
|
|
# CONSTANT = 120
|
|
# IDX_SIZE = 16
|
|
# MANT_WIDTH = 52
|
|
# EXP_WIDTH = 11
|
|
# MID_INT_SIZE = 192
|
|
|
|
# Values for 128 bit float
|
|
CONSTANT = 120
|
|
IDX_SIZE = 128
|
|
MANT_WIDTH = 112
|
|
EXP_WIDTH = 15
|
|
MID_INT_SIZE = 256 + 64
|
|
|
|
MAX_EXP = 2 ** (EXP_WIDTH - 1)
|
|
POSITIVE_ARR_SIZE = MAX_EXP // IDX_SIZE
|
|
NEGATIVE_ARR_SIZE = (MAX_EXP // IDX_SIZE) + ((MANT_WIDTH + (IDX_SIZE - 1)) // IDX_SIZE)
|
|
MOD_SIZE = (10**BLOCK_SIZE) << (CONSTANT + (IDX_SIZE if IDX_SIZE > 1 else 0))
|
|
|
|
|
|
# floor(5^(-9i) * 2^(e + c_1 - 9i) + 1) % (10^9 * 2^c_1)
|
|
def get_table_positive(exponent, i):
|
|
pow_of_two = 1 << (exponent + CONSTANT - (BLOCK_SIZE * i))
|
|
pow_of_five = 5 ** (BLOCK_SIZE * i)
|
|
result = (pow_of_two // pow_of_five) + 1
|
|
return result % MOD_SIZE
|
|
|
|
|
|
# floor(10^(9*(-i)) * 2^(c_0 + (-e))) % (10^9 * 2^c_0)
|
|
def get_table_negative(exponent, i):
|
|
result = 1
|
|
pow_of_ten = 10 ** (BLOCK_SIZE * i)
|
|
shift_amount = CONSTANT - exponent
|
|
if shift_amount < 0:
|
|
result = pow_of_ten >> (-shift_amount)
|
|
else:
|
|
result = pow_of_ten << (shift_amount)
|
|
return result % MOD_SIZE
|
|
|
|
|
|
# Returns floor(log_10(2^e)); requires 0 <= e <= 42039.
|
|
def ceil_log10_pow2(e):
|
|
return ((e * 0x13441350FBD) >> 42) + 1
|
|
|
|
|
|
def length_for_num(idx, index_size=IDX_SIZE):
|
|
return (
|
|
ceil_log10_pow2(idx * index_size) + ceil_log10_pow2(MANT_WIDTH) + BLOCK_SIZE - 1
|
|
) // BLOCK_SIZE
|
|
|
|
|
|
def get_64bit_window(num, index):
|
|
return (num >> (index * 64)) % (2**64)
|
|
|
|
|
|
def mid_int_to_str(num):
|
|
outstr = " {"
|
|
outstr += str(get_64bit_window(num, 0)) + "u"
|
|
for i in range(1, MID_INT_SIZE // 64):
|
|
outstr += ", " + str(get_64bit_window(num, i)) + "u"
|
|
outstr += "},"
|
|
return outstr
|
|
|
|
|
|
def print_positive_table_for_idx(idx):
|
|
positive_blocks = length_for_num(idx)
|
|
for i in range(positive_blocks):
|
|
table_val = get_table_positive(idx * IDX_SIZE, i)
|
|
# print(hex(table_val))
|
|
print(mid_int_to_str(table_val))
|
|
return positive_blocks
|
|
|
|
|
|
def print_negative_table_for_idx(idx):
|
|
i = 0
|
|
min_block = -1
|
|
table_val = 0
|
|
MIN_USEFUL_VAL = 2 ** (CONSTANT - (MANT_WIDTH + 2))
|
|
# Iterate through the zero blocks
|
|
while table_val < MIN_USEFUL_VAL:
|
|
i += 1
|
|
table_val = get_table_negative((idx) * IDX_SIZE, i)
|
|
else:
|
|
i -= 1
|
|
|
|
min_block = i
|
|
|
|
# Iterate until another zero block is found
|
|
while table_val >= MIN_USEFUL_VAL:
|
|
table_val = get_table_negative((idx) * IDX_SIZE, i + 1)
|
|
if table_val >= MIN_USEFUL_VAL:
|
|
print(mid_int_to_str(table_val))
|
|
i += 1
|
|
return i - min_block, min_block
|
|
|
|
|
|
positive_size_arr = [0] * (POSITIVE_ARR_SIZE + 1)
|
|
|
|
negative_size_arr = [0] * (NEGATIVE_ARR_SIZE + 1)
|
|
min_block_arr = [0] * (NEGATIVE_ARR_SIZE + 1)
|
|
acc = 0
|
|
|
|
if MOD_SIZE > (2**MID_INT_SIZE):
|
|
print(
|
|
"Mod size is too big for current MID_INT_SIZE by a factor of",
|
|
MOD_SIZE // (2**MID_INT_SIZE),
|
|
)
|
|
else:
|
|
print("static const uint64_t POW10_SPLIT[][" + str(MID_INT_SIZE // 64) + "] = {")
|
|
for idx in range(0, POSITIVE_ARR_SIZE + 1):
|
|
num_size = print_positive_table_for_idx(idx)
|
|
positive_size_arr[idx] = acc
|
|
acc += num_size
|
|
print("};")
|
|
|
|
print(
|
|
"static const uint32_t POW10_OFFSET_2[" + str(len(positive_size_arr)) + "] = {",
|
|
str(positive_size_arr)[1:-2],
|
|
"};",
|
|
)
|
|
|
|
print("static const uint64_t POW10_SPLIT_2[][" + str(MID_INT_SIZE // 64) + "] = {")
|
|
for idx in range(0, NEGATIVE_ARR_SIZE):
|
|
num_size, min_block = print_negative_table_for_idx(idx)
|
|
acc += num_size
|
|
negative_size_arr[idx + 1] = acc
|
|
min_block_arr[idx] = min_block
|
|
print("};")
|
|
print(
|
|
"static const uint32_t POW10_OFFSET_2[" + str(len(negative_size_arr)) + "] = {",
|
|
str(negative_size_arr)[1:-2],
|
|
"};",
|
|
)
|
|
print(
|
|
"static const uint16_t MIN_BLOCK_2[" + str(len(min_block_arr)) + "] = {",
|
|
str(min_block_arr)[1:-2],
|
|
"};",
|
|
)
|