aboutsummaryrefslogtreecommitdiff
path: root/math/gen-auto-libm-tests.c
diff options
context:
space:
mode:
Diffstat (limited to 'math/gen-auto-libm-tests.c')
-rw-r--r--math/gen-auto-libm-tests.c160
1 files changed, 136 insertions, 24 deletions
diff --git a/math/gen-auto-libm-tests.c b/math/gen-auto-libm-tests.c
index 934c64b..442a10d 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);
}