diff options
Diffstat (limited to 'math/gen-auto-libm-tests.c')
-rw-r--r-- | math/gen-auto-libm-tests.c | 160 |
1 files changed, 136 insertions, 24 deletions
diff --git a/math/gen-auto-libm-tests.c b/math/gen-auto-libm-tests.c index 934c64bc76..442a10d5e6 100644 --- a/math/gen-auto-libm-tests.c +++ b/math/gen-auto-libm-tests.c @@ -25,6 +25,15 @@ gen-auto-libm-tests auto-libm-test-in <func> auto-libm-test-out-<func> + to generate results for normal libm functions, or + + gen-auto-libm-tests --narrow auto-libm-test-in <func> \ + auto-libm-test-out-narrow-<func> + + to generate results for a function rounding results to a narrower + type (in the case of fma and sqrt, both output files are generated + from the same test inputs). + The input file auto-libm-test-in contains three kinds of lines: Lines beginning with "#" are comments, and are ignored, as are @@ -120,7 +129,22 @@ missing or spurious, or because the calculation of correct results indicated it was optional). Conditions "before-rounding" and "after-rounding" indicate tests where expectations for underflow - exceptions depend on how the architecture detects tininess. */ + exceptions depend on how the architecture detects tininess. + + For functions rounding their results to a narrower type, the format + given on an output test line is the result format followed by + information about the requirements on the argument format to be + able to represent the argument values, in the form + "format:arg_fmt(MAX_EXP,NUM_ONES,MIN_EXP,MAX_PREC)". Instead of + separate lines for separate argument formats, an output test line + relates to all argument formats that can represent the values. + MAX_EXP is the maximum exponent of a nonzero bit in any argument, + or 0 if all arguments are zero; NUM_ONES is the maximum number of + leading bits with value 1 in an argument with exponent MAX_EXP, or + 0 if all arguments are zero; MIN_EXP is the minimum exponent of a + nonzero bit in any argument, or 0 if all arguments are zero; + MAX_PREC is the maximum precision required to represent all + arguments, or 0 if all arguments are zero. */ #define _GNU_SOURCE @@ -1718,12 +1742,13 @@ output_generic_value (FILE *fp, const char *filename, const generic_value *v, } } -/* Generate test output to FP (name FILENAME) for test function TF, - input test IT, choice of input values INPUTS. */ +/* Generate test output to FP (name FILENAME) for test function TF + (rounding results to a narrower type if NARROW), input test IT, + choice of input values INPUTS. */ static void output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, - input_test *it, generic_value *inputs) + bool narrow, input_test *it, generic_value *inputs) { bool long_bits_matters = false; bool fits_long32 = true; @@ -1794,24 +1819,81 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, mpfr_t res[rm_num_modes]; unsigned int exc_before[rm_num_modes]; unsigned int exc_after[rm_num_modes]; + bool have_fp_arg = false; + int max_exp = 0; + int num_ones = 0; + int min_exp = 0; + int max_prec = 0; for (size_t i = 0; i < tf->num_args; i++) { if (inputs[i].type == gtype_fp) { - round_real (res, exc_before, exc_after, inputs[i].value.f, - f); - if (!mpfr_equal_p (res[rm_tonearest], inputs[i].value.f)) - fits = false; - for (rounding_mode m = rm_first_mode; m < rm_num_modes; m++) - mpfr_clear (res[m]); - if (!fits) - break; + if (narrow) + { + if (mpfr_zero_p (inputs[i].value.f)) + continue; + assert (mpfr_regular_p (inputs[i].value.f)); + int this_exp, this_num_ones, this_min_exp, this_prec; + mpz_t tmp; + mpz_init (tmp); + mpfr_exp_t e = mpfr_get_z_2exp (tmp, inputs[i].value.f); + if (mpz_sgn (tmp) < 0) + mpz_neg (tmp, tmp); + size_t bits = mpz_sizeinbase (tmp, 2); + mp_bitcnt_t tz = mpz_scan1 (tmp, 0); + this_min_exp = e + tz; + this_prec = bits - tz; + assert (this_prec > 0); + this_exp = this_min_exp + this_prec - 1; + assert (this_exp + == mpfr_get_exp (inputs[i].value.f) - 1); + this_num_ones = 1; + while ((size_t) this_num_ones < bits + && mpz_tstbit (tmp, bits - 1 - this_num_ones)) + this_num_ones++; + mpz_clear (tmp); + if (have_fp_arg) + { + if (this_exp > max_exp + || (this_exp == max_exp + && this_num_ones > num_ones)) + { + max_exp = this_exp; + num_ones = this_num_ones; + } + if (this_min_exp < min_exp) + min_exp = this_min_exp; + if (this_prec > max_prec) + max_prec = this_prec; + } + else + { + max_exp = this_exp; + num_ones = this_num_ones; + min_exp = this_min_exp; + max_prec = this_prec; + } + have_fp_arg = true; + } + else + { + round_real (res, exc_before, exc_after, + inputs[i].value.f, f); + if (!mpfr_equal_p (res[rm_tonearest], inputs[i].value.f)) + fits = false; + for (rounding_mode m = rm_first_mode; + m < rm_num_modes; + m++) + mpfr_clear (res[m]); + if (!fits) + break; + } } } if (!fits) continue; - /* The inputs fit this type, so compute the ideal outputs - and exceptions. */ + /* The inputs fit this type if required to do so, so compute + the ideal outputs and exceptions. */ mpfr_t all_res[MAX_NRET][rm_num_modes]; unsigned int all_exc_before[MAX_NRET][rm_num_modes]; unsigned int all_exc_after[MAX_NRET][rm_num_modes]; @@ -1895,10 +1977,21 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, assert ((merged_exc_before[m] & (1U << exc_underflow)) != 0); } unsigned int merged_exc = merged_exc_before[m]; - if (fprintf (fp, "= %s %s %s%s", tf->name, - rounding_modes[m].name, fp_formats[f].name, - long_cond) < 0) - error (EXIT_FAILURE, errno, "write to '%s'", filename); + if (narrow) + { + if (fprintf (fp, "= %s %s %s%s:arg_fmt(%d,%d,%d,%d)", + tf->name, rounding_modes[m].name, + fp_formats[f].name, long_cond, max_exp, + num_ones, min_exp, max_prec) < 0) + error (EXIT_FAILURE, errno, "write to '%s'", filename); + } + else + { + if (fprintf (fp, "= %s %s %s%s", tf->name, + rounding_modes[m].name, fp_formats[f].name, + long_cond) < 0) + error (EXIT_FAILURE, errno, "write to '%s'", filename); + } /* Print inputs. */ for (size_t i = 0; i < tf->num_args; i++) output_generic_value (fp, filename, &inputs[i], false, @@ -2158,10 +2251,12 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, generic_value_free (&generic_outputs[i]); } -/* Generate test output data for FUNCTION to FILENAME. */ +/* Generate test output data for FUNCTION to FILENAME. The function + is interpreted as rounding its results to a narrower type if + NARROW. */ static void -generate_output (const char *function, const char *filename) +generate_output (const char *function, bool narrow, const char *filename) { FILE *fp = fopen (filename, "w"); if (fp == NULL) @@ -2177,7 +2272,8 @@ generate_output (const char *function, const char *filename) if (fputs (it->line, fp) < 0) error (EXIT_FAILURE, errno, "write to '%s'", filename); for (size_t k = 0; k < it->num_input_cases; k++) - output_for_one_input_case (fp, filename, tf, it, it->inputs[k]); + output_for_one_input_case (fp, filename, tf, narrow, + it, it->inputs[k]); } } if (fclose (fp) != 0) @@ -2187,14 +2283,30 @@ generate_output (const char *function, const char *filename) int main (int argc, char **argv) { - if (argc != 4) + if (argc != 4 + && !(argc == 5 && strcmp (argv[1], "--narrow") == 0)) error (EXIT_FAILURE, 0, - "usage: gen-auto-libm-tests <input> <func> <output>"); + "usage: gen-auto-libm-tests [--narrow] <input> <func> <output>"); + bool narrow; const char *input_filename = argv[1]; const char *function = argv[2]; const char *output_filename = argv[3]; + if (argc == 4) + { + narrow = false; + input_filename = argv[1]; + function = argv[2]; + output_filename = argv[3]; + } + else + { + narrow = true; + input_filename = argv[2]; + function = argv[3]; + output_filename = argv[4]; + } init_fp_formats (); read_input (input_filename); - generate_output (function, output_filename); + generate_output (function, narrow, output_filename); exit (EXIT_SUCCESS); } |