]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - math/gen-auto-libm-tests.c
Fix the inaccuracy of j0f/j1f/y0f/y1f [BZ #14469, #14470, #14471, #14472]
[thirdparty/glibc.git] / math / gen-auto-libm-tests.c
index cae2f72cb8eb984e6c4089dd30f9459d1cf4abe7..95e05635d3241df3c1329d180d837b8fa7d460bb 100644 (file)
@@ -1,5 +1,5 @@
 /* Generate expected output for libm tests with MPFR and MPC.
-   Copyright (C) 2013-2017 Free Software Foundation, Inc.
+   Copyright (C) 2013-2021 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -14,7 +14,7 @@
 
    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, see
-   <http://www.gnu.org/licenses/>.  */
+   <https://www.gnu.org/licenses/>.  */
 
 /* Compile this program as:
 
 
    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
@@ -83,8 +92,7 @@
    permitted.  In other cases (where no overflow or underflow is
    permitted), errno is expected to be left unchanged.
 
-   The flag "no-test-inline" indicates a test is disabled for inline
-   function testing; "ignore-zero-inf-sign" indicates the the signs of
+   The flag "ignore-zero-inf-sign" indicates the the signs of
    zero and infinite results should be ignored; "xfail" indicates the
    test is disabled as expected to produce incorrect results,
    "xfail-rounding" indicates the test is disabled only in rounding
    plus_infty or minus_infty for infinite expected results, or as
    integer constant expressions (not necessarily with the right type)
    or IGNORE for integer inputs and outputs.  Flags are
-   "no-test-inline", "ignore-zero-info-sign", "xfail", "<exception>",
+   "ignore-zero-info-sign", "xfail", "<exception>",
    "<exception>-ok", "errno-<value>", "errno-<value>-ok", which may be
    unconditional or conditional.  "<exception>" indicates that a
    correct result means the given exception should be raised.
    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
 
@@ -312,7 +335,6 @@ typedef struct
 /* A type of input flag.  */
 typedef enum
   {
-    flag_no_test_inline,
     flag_ignore_zero_inf_sign,
     flag_xfail,
     flag_xfail_rounding,
@@ -340,7 +362,6 @@ typedef enum
    enumeration.  */
 static const char *const input_flags[num_input_flag_types] =
   {
-    "no-test-inline",
     "ignore-zero-inf-sign",
     "xfail",
     "xfail-rounding",
@@ -507,6 +528,7 @@ static test_function test_functions[] =
   {
     FUNC_mpfr_f_f ("acos", mpfr_acos, false),
     FUNC_mpfr_f_f ("acosh", mpfr_acosh, false),
+    FUNC_mpfr_ff_f ("add", mpfr_add, true),
     FUNC_mpfr_f_f ("asin", mpfr_asin, false),
     FUNC_mpfr_f_f ("asinh", mpfr_asinh, false),
     FUNC_mpfr_f_f ("atan", mpfr_atan, false),
@@ -536,6 +558,7 @@ static test_function test_functions[] =
     FUNC_mpc_c_c ("csqrt", mpc_sqrt, false),
     FUNC_mpc_c_c ("ctan", mpc_tan, false),
     FUNC_mpc_c_c ("ctanh", mpc_tanh, false),
+    FUNC_mpfr_ff_f ("div", mpfr_div, true),
     FUNC_mpfr_f_f ("erf", mpfr_erf, false),
     FUNC_mpfr_f_f ("erfc", mpfr_erfc, false),
     FUNC_mpfr_f_f ("exp", mpfr_exp, false),
@@ -554,11 +577,13 @@ static test_function test_functions[] =
     FUNC_mpfr_f_f ("log10", mpfr_log10, false),
     FUNC_mpfr_f_f ("log1p", mpfr_log1p, false),
     FUNC_mpfr_f_f ("log2", mpfr_log2, false),
+    FUNC_mpfr_ff_f ("mul", mpfr_mul, true),
     FUNC_mpfr_ff_f ("pow", mpfr_pow, false),
     FUNC_mpfr_f_f ("sin", mpfr_sin, false),
     FUNC ("sincos", ARGS1 (type_fp), RET2 (type_fp, type_fp), false, false,
          false, CALC (mpfr_f_11, mpfr_sin_cos)),
     FUNC_mpfr_f_f ("sinh", mpfr_sinh, false),
+    FUNC_mpfr_ff_f ("sub", mpfr_sub, true),
     FUNC_mpfr_f_f ("sqrt", mpfr_sqrt, true),
     FUNC_mpfr_f_f ("tan", mpfr_tan, false),
     FUNC_mpfr_f_f ("tanh", mpfr_tanh, false),
@@ -1270,7 +1295,7 @@ handle_input_flag (char *arg, input_flag *flag,
   char c = *ep;
   *ep = 0;
   bool found = false;
-  for (input_flag_type i = flag_first_flag; i <= num_input_flag_types; i++)
+  for (input_flag_type i = flag_first_flag; i < num_input_flag_types; i++)
     {
       if (strcmp (arg, input_flags[i]) == 0)
        {
@@ -1718,12 +1743,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 +1820,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 +1978,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,
@@ -1953,7 +2047,6 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf,
              for (size_t i = 0; i < it->num_flags; i++)
                switch (it->flags[i].type)
                  {
-                 case flag_no_test_inline:
                  case flag_ignore_zero_inf_sign:
                  case flag_xfail:
                    if (fprintf (fp, " %s%s",
@@ -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);
 }