| """ | |
| //===-- 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], | |
| "};", | |
| ) |