about summary refs log tree commit diff
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 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);
 }