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.c148
1 files changed, 115 insertions, 33 deletions
diff --git a/math/gen-auto-libm-tests.c b/math/gen-auto-libm-tests.c
index 198d001519..eeec2ab6f0 100644
--- a/math/gen-auto-libm-tests.c
+++ b/math/gen-auto-libm-tests.c
@@ -210,16 +210,18 @@ typedef struct
   const char *name;
   /* The MPFR rounding mode.  */
   mpfr_rnd_t mpfr_mode;
+  /* The MPC rounding mode.  */
+  mpc_rnd_t mpc_mode;
 } rounding_mode_desc;
 
 /* List of rounding modes, in the same order as the rounding_mode
    enumeration.  */
 static const rounding_mode_desc rounding_modes[rm_num_modes] =
   {
-    { "downward", MPFR_RNDD },
-    { "tonearest", MPFR_RNDN },
-    { "towardzero", MPFR_RNDZ },
-    { "upward", MPFR_RNDU },
+    { "downward", MPFR_RNDD, MPC_RNDDD },
+    { "tonearest", MPFR_RNDN, MPC_RNDNN },
+    { "towardzero", MPFR_RNDZ, MPC_RNDZZ },
+    { "upward", MPFR_RNDU, MPC_RNDUU },
   };
 
 /* The supported exceptions.  */
@@ -394,6 +396,8 @@ typedef enum
     mpfr_f_f,
     /* MPFR function with two arguments and one result.  */
     mpfr_ff_f,
+    /* MPFR function with three arguments and one result.  */
+    mpfr_fff_f,
     /* MPFR function with a single argument and floating-point and
        integer results.  */
     mpfr_f_f1,
@@ -424,6 +428,8 @@ typedef struct
   {
     int (*mpfr_f_f) (mpfr_t, const mpfr_t, mpfr_rnd_t);
     int (*mpfr_ff_f) (mpfr_t, const mpfr_t, const mpfr_t, mpfr_rnd_t);
+    int (*mpfr_fff_f) (mpfr_t, const mpfr_t, const mpfr_t, const mpfr_t,
+		       mpfr_rnd_t);
     int (*mpfr_f_f1) (mpfr_t, int *, const mpfr_t, mpfr_rnd_t);
     int (*mpfr_if_f) (mpfr_t, long, const mpfr_t, mpfr_rnd_t);
     int (*mpfr_f_11) (mpfr_t, mpfr_t, const mpfr_t, mpfr_rnd_t);
@@ -452,6 +458,10 @@ typedef struct
   /* Whether the function is a complex function, so errno setting is
      optional.  */
   bool complex_fn;
+  /* Whether to treat arguments given as floating-point constants as
+     exact only, rather than rounding them up and down to all
+     formats.  */
+  bool exact_args;
   /* How to calculate this function.  */
   func_calc_desc calc;
   /* The number of tests allocated for this function.  */
@@ -469,26 +479,26 @@ typedef struct
 #define RET1(T1) 1, { T1 }
 #define RET2(T1, T2) 2, { T1, T2 }
 #define CALC(TYPE, FN) { TYPE, { .TYPE = FN } }
-#define FUNC(NAME, ARGS, RET, EXACT, COMPLEX_FN, CALC)		\
-  {								\
-    NAME, ARGS, RET, EXACT, COMPLEX_FN, CALC, 0, 0, NULL	\
+#define FUNC(NAME, ARGS, RET, EXACT, COMPLEX_FN, EXACT_ARGS, CALC)	\
+  {									\
+    NAME, ARGS, RET, EXACT, COMPLEX_FN, EXACT_ARGS, CALC, 0, 0, NULL	\
   }
 
-#define FUNC_mpfr_f_f(NAME, MPFR_FUNC, EXACT)			\
-  FUNC (NAME, ARGS1 (type_fp), RET1 (type_fp), EXACT, false,	\
+#define FUNC_mpfr_f_f(NAME, MPFR_FUNC, EXACT)				\
+  FUNC (NAME, ARGS1 (type_fp), RET1 (type_fp), EXACT, false, false,	\
 	CALC (mpfr_f_f, MPFR_FUNC))
 #define FUNC_mpfr_ff_f(NAME, MPFR_FUNC, EXACT)				\
   FUNC (NAME, ARGS2 (type_fp, type_fp), RET1 (type_fp), EXACT, false,	\
-	CALC (mpfr_ff_f, MPFR_FUNC))
+	false, CALC (mpfr_ff_f, MPFR_FUNC))
 #define FUNC_mpfr_if_f(NAME, MPFR_FUNC, EXACT)				\
   FUNC (NAME, ARGS2 (type_int, type_fp), RET1 (type_fp), EXACT, false,	\
-	CALC (mpfr_if_f, MPFR_FUNC))
+	false, CALC (mpfr_if_f, MPFR_FUNC))
 #define FUNC_mpc_c_f(NAME, MPFR_FUNC, EXACT)				\
   FUNC (NAME, ARGS2 (type_fp, type_fp), RET1 (type_fp), EXACT, true,	\
-	CALC (mpc_c_f, MPFR_FUNC))
+	false, CALC (mpc_c_f, MPFR_FUNC))
 #define FUNC_mpc_c_c(NAME, MPFR_FUNC, EXACT)				\
   FUNC (NAME, ARGS2 (type_fp, type_fp), RET2 (type_fp, type_fp), EXACT, \
-	true, CALC (mpc_c_c, MPFR_FUNC))
+	true, false, CALC (mpc_c_c, MPFR_FUNC))
 
 /* List of functions handled by this program.  */
 static test_function test_functions[] =
@@ -517,7 +527,8 @@ static test_function test_functions[] =
     FUNC_mpfr_f_f ("cos", mpfr_cos, false),
     FUNC_mpfr_f_f ("cosh", mpfr_cosh, false),
     FUNC ("cpow", ARGS4 (type_fp, type_fp, type_fp, type_fp),
-	  RET2 (type_fp, type_fp), false, true, CALC (mpc_cc_c, mpc_pow)),
+	  RET2 (type_fp, type_fp), false, true, false,
+	  CALC (mpc_cc_c, mpc_pow)),
     FUNC_mpc_c_c ("csin", mpc_sin, false),
     FUNC_mpc_c_c ("csinh", mpc_sinh, false),
     FUNC_mpc_c_c ("csqrt", mpc_sqrt, false),
@@ -529,12 +540,14 @@ static test_function test_functions[] =
     FUNC_mpfr_f_f ("exp10", mpfr_exp10, false),
     FUNC_mpfr_f_f ("exp2", mpfr_exp2, false),
     FUNC_mpfr_f_f ("expm1", mpfr_expm1, false),
+    FUNC ("fma", ARGS3 (type_fp, type_fp, type_fp), RET1 (type_fp),
+	  true, false, true, CALC (mpfr_fff_f, mpfr_fma)),
     FUNC_mpfr_ff_f ("hypot", mpfr_hypot, false),
     FUNC_mpfr_f_f ("j0", mpfr_j0, false),
     FUNC_mpfr_f_f ("j1", mpfr_j1, false),
     FUNC_mpfr_if_f ("jn", mpfr_jn, false),
     FUNC ("lgamma", ARGS1 (type_fp), RET2 (type_fp, type_int), false, false,
-	  CALC (mpfr_f_f1, mpfr_lgamma)),
+	  false, CALC (mpfr_f_f1, mpfr_lgamma)),
     FUNC_mpfr_f_f ("log", mpfr_log, false),
     FUNC_mpfr_f_f ("log10", mpfr_log10, false),
     FUNC_mpfr_f_f ("log1p", mpfr_log1p, false),
@@ -542,7 +555,7 @@ static test_function test_functions[] =
     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,
-	  CALC (mpfr_f_11, mpfr_sin_cos)),
+	  false, CALC (mpfr_f_11, mpfr_sin_cos)),
     FUNC_mpfr_f_f ("sinh", mpfr_sinh, false),
     FUNC_mpfr_f_f ("sqrt", mpfr_sqrt, true),
     FUNC_mpfr_f_f ("tan", mpfr_tan, false),
@@ -1092,16 +1105,19 @@ round_real (mpfr_t res[rm_num_modes],
 
 /* Handle the input argument at ARG (NUL-terminated), updating the
    lists of test inputs in IT accordingly.  NUM_PREV_ARGS arguments
-   are already in those lists.  The argument, of type GTYPE, comes
-   from file FILENAME, line LINENO.  */
+   are already in those lists.  If EXACT_ARGS, interpret a value given
+   as a floating-point constant exactly (it must be exact for some
+   supported format) rather than rounding up and down.  The argument,
+   of type GTYPE, comes from file FILENAME, line LINENO.  */
 
 static void
 handle_input_arg (const char *arg, input_test *it, size_t num_prev_args,
-		  generic_value_type gtype,
+		  generic_value_type gtype, bool exact_args,
 		  const char *filename, unsigned int lineno)
 {
   size_t num_values = 0;
   generic_value values[2 * fp_num_formats];
+  bool check_empty_list = false;
   switch (gtype)
     {
     case gtype_fp:
@@ -1125,6 +1141,8 @@ handle_input_arg (const char *arg, input_test *it, size_t num_prev_args,
 	    {
 	      mpfr_t tmp;
 	      char *ep;
+	      if (exact_args)
+		check_empty_list = true;
 	      mpfr_init (tmp);
 	      bool inexact = mpfr_strtofr (tmp, arg, &ep, 0, MPFR_RNDZ);
 	      if (*ep != 0 || !mpfr_number_p (tmp))
@@ -1136,7 +1154,9 @@ handle_input_arg (const char *arg, input_test *it, size_t num_prev_args,
 	      unsigned int exc_after[rm_num_modes];
 	      round_real (rounded, exc_before, exc_after, tmp, f);
 	      mpfr_clear (tmp);
-	      if (mpfr_number_p (rounded[rm_upward]))
+	      if (mpfr_number_p (rounded[rm_upward])
+		  && (!exact_args || mpfr_equal_p (rounded[rm_upward],
+						   rounded[rm_downward])))
 		{
 		  mpfr_init2 (extra_values[num_extra_values],
 			      fp_formats[f].mant_dig);
@@ -1144,7 +1164,7 @@ handle_input_arg (const char *arg, input_test *it, size_t num_prev_args,
 					  rounded[rm_upward], MPFR_RNDN));
 		  num_extra_values++;
 		}
-	      if (mpfr_number_p (rounded[rm_downward]))
+	      if (mpfr_number_p (rounded[rm_downward]) && !exact_args)
 		{
 		  mpfr_init2 (extra_values[num_extra_values],
 			      fp_formats[f].mant_dig);
@@ -1195,6 +1215,10 @@ handle_input_arg (const char *arg, input_test *it, size_t num_prev_args,
     default:
       abort ();
     }
+  if (check_empty_list && num_values == 0)
+    error_at_line (EXIT_FAILURE, 0, filename, lineno,
+		   "floating-point argument not exact for any format: '%s'",
+		   arg);
   assert (num_values > 0 && num_values <= ARRAY_SIZE (values));
   if (it->num_input_cases >= SIZE_MAX / num_values)
     error_at_line (EXIT_FAILURE, 0, filename, lineno, "too many input cases");
@@ -1319,7 +1343,7 @@ add_test (char *line, const char *filename, unsigned int lineno)
 	      *ep = 0;
 	      handle_input_arg (p, it, j,
 				generic_arg_ret_type (tf->arg_types[j]),
-				filename, lineno);
+				tf->exact_args, filename, lineno);
 	      *ep = c;
 	      p = ep + 1;
 	    }
@@ -1383,15 +1407,19 @@ read_input (const char *filename)
 }
 
 /* Calculate the generic results (round-to-zero with sticky bit) for
-   the function described by CALC, with inputs INPUTS.  */
+   the function described by CALC, with inputs INPUTS, if MODE is
+   rm_towardzero; for other modes, calculate results in that mode,
+   which must be exact zero results.  */
 
 static void
 calc_generic_results (generic_value *outputs, generic_value *inputs,
-		      const func_calc_desc *calc)
+		      const func_calc_desc *calc, rounding_mode mode)
 {
   bool inexact;
   int mpc_ternary;
   mpc_t ci1, ci2, co;
+  mpfr_rnd_t mode_mpfr = rounding_modes[mode].mpfr_mode;
+  mpc_rnd_t mode_mpc = rounding_modes[mode].mpc_mode;
 
   switch (calc->method)
     {
@@ -1400,7 +1428,9 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
       outputs[0].type = gtype_fp;
       mpfr_init (outputs[0].value.f);
       inexact = calc->func.mpfr_f_f (outputs[0].value.f, inputs[0].value.f,
-				     MPFR_RNDZ);
+				     mode_mpfr);
+      if (mode != rm_towardzero)
+	assert (!inexact && mpfr_zero_p (outputs[0].value.f));
       adjust_real (outputs[0].value.f, inexact);
       break;
 
@@ -1410,7 +1440,23 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
       outputs[0].type = gtype_fp;
       mpfr_init (outputs[0].value.f);
       inexact = calc->func.mpfr_ff_f (outputs[0].value.f, inputs[0].value.f,
-				      inputs[1].value.f, MPFR_RNDZ);
+				      inputs[1].value.f, mode_mpfr);
+      if (mode != rm_towardzero)
+	assert (!inexact && mpfr_zero_p (outputs[0].value.f));
+      adjust_real (outputs[0].value.f, inexact);
+      break;
+
+    case mpfr_fff_f:
+      assert (inputs[0].type == gtype_fp);
+      assert (inputs[1].type == gtype_fp);
+      assert (inputs[2].type == gtype_fp);
+      outputs[0].type = gtype_fp;
+      mpfr_init (outputs[0].value.f);
+      inexact = calc->func.mpfr_fff_f (outputs[0].value.f, inputs[0].value.f,
+				       inputs[1].value.f, inputs[2].value.f,
+				       mode_mpfr);
+      if (mode != rm_towardzero)
+	assert (!inexact && mpfr_zero_p (outputs[0].value.f));
       adjust_real (outputs[0].value.f, inexact);
       break;
 
@@ -1421,7 +1467,9 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
       mpfr_init (outputs[0].value.f);
       int i = 0;
       inexact = calc->func.mpfr_f_f1 (outputs[0].value.f, &i,
-				      inputs[0].value.f, MPFR_RNDZ);
+				      inputs[0].value.f, mode_mpfr);
+      if (mode != rm_towardzero)
+	assert (!inexact && mpfr_zero_p (outputs[0].value.f));
       adjust_real (outputs[0].value.f, inexact);
       mpz_init_set_si (outputs[1].value.i, i);
       break;
@@ -1434,7 +1482,9 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
       assert (mpz_fits_slong_p (inputs[0].value.i));
       long l = mpz_get_si (inputs[0].value.i);
       inexact = calc->func.mpfr_if_f (outputs[0].value.f, l,
-				      inputs[1].value.f, MPFR_RNDZ);
+				      inputs[1].value.f, mode_mpfr);
+      if (mode != rm_towardzero)
+	assert (!inexact && mpfr_zero_p (outputs[0].value.f));
       adjust_real (outputs[0].value.f, inexact);
       break;
 
@@ -1447,7 +1497,12 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
       int comb_ternary = calc->func.mpfr_f_11 (outputs[0].value.f,
 					       outputs[1].value.f,
 					       inputs[0].value.f,
-					       MPFR_RNDZ);
+					       mode_mpfr);
+      if (mode != rm_towardzero)
+	assert (((comb_ternary & 0x3) == 0
+		 && mpfr_zero_p (outputs[0].value.f))
+		|| ((comb_ternary & 0xc) == 0
+		    && mpfr_zero_p (outputs[1].value.f)));
       adjust_real (outputs[0].value.f, (comb_ternary & 0x3) != 0);
       adjust_real (outputs[1].value.f, (comb_ternary & 0xc) != 0);
       break;
@@ -1460,7 +1515,9 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
       mpc_init2 (ci1, internal_precision);
       assert_exact (mpc_set_fr_fr (ci1, inputs[0].value.f, inputs[1].value.f,
 				   MPC_RNDNN));
-      inexact = calc->func.mpc_c_f (outputs[0].value.f, ci1, MPFR_RNDZ);
+      inexact = calc->func.mpc_c_f (outputs[0].value.f, ci1, mode_mpfr);
+      if (mode != rm_towardzero)
+	assert (!inexact && mpfr_zero_p (outputs[0].value.f));
       adjust_real (outputs[0].value.f, inexact);
       mpc_clear (ci1);
       break;
@@ -1476,7 +1533,12 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
       mpc_init2 (co, internal_precision);
       assert_exact (mpc_set_fr_fr (ci1, inputs[0].value.f, inputs[1].value.f,
 				   MPC_RNDNN));
-      mpc_ternary = calc->func.mpc_c_c (co, ci1, MPC_RNDZZ);
+      mpc_ternary = calc->func.mpc_c_c (co, ci1, mode_mpc);
+      if (mode != rm_towardzero)
+	assert ((!MPC_INEX_RE (mpc_ternary)
+		 && mpfr_zero_p (mpc_realref (co)))
+		|| (!MPC_INEX_IM (mpc_ternary)
+		    && mpfr_zero_p (mpc_imagref (co))));
       assert_exact (mpfr_set (outputs[0].value.f, mpc_realref (co),
 			      MPFR_RNDN));
       assert_exact (mpfr_set (outputs[1].value.f, mpc_imagref (co),
@@ -1503,7 +1565,12 @@ calc_generic_results (generic_value *outputs, generic_value *inputs,
 				   MPC_RNDNN));
       assert_exact (mpc_set_fr_fr (ci2, inputs[2].value.f, inputs[3].value.f,
 				   MPC_RNDNN));
-      mpc_ternary = calc->func.mpc_cc_c (co, ci1, ci2, MPC_RNDZZ);
+      mpc_ternary = calc->func.mpc_cc_c (co, ci1, ci2, mode_mpc);
+      if (mode != rm_towardzero)
+	assert ((!MPC_INEX_RE (mpc_ternary)
+		 && mpfr_zero_p (mpc_realref (co)))
+		|| (!MPC_INEX_IM (mpc_ternary)
+		    && mpfr_zero_p (mpc_imagref (co))));
       assert_exact (mpfr_set (outputs[0].value.f, mpc_realref (co),
 			      MPFR_RNDN));
       assert_exact (mpfr_set (outputs[1].value.f, mpc_imagref (co),
@@ -1674,7 +1741,7 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf,
 	}
     }
   generic_value generic_outputs[MAX_NRET];
-  calc_generic_results (generic_outputs, inputs, &tf->calc);
+  calc_generic_results (generic_outputs, inputs, &tf->calc, rm_towardzero);
   bool ignore_output_long32[MAX_NRET] = { false };
   bool ignore_output_long64[MAX_NRET] = { false };
   for (size_t i = 0; i < tf->num_ret; i++)
@@ -1777,6 +1844,21 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf,
 				&& mpfr_cmpabs (generic_outputs[i].value.f,
 						fp_formats[f].min) <= 0);
 			}
+		      /* If the result is an exact zero, the sign may
+			 depend on the rounding mode, so recompute it
+			 directly in that mode.  */
+		      if (mpfr_zero_p (all_res[i][m])
+			  && (all_exc_before[i][m] & (1U << exc_inexact)) == 0)
+			{
+			  generic_value outputs_rm[MAX_NRET];
+			  calc_generic_results (outputs_rm, inputs,
+						&tf->calc, m);
+			  assert_exact (mpfr_set (all_res[i][m],
+						  outputs_rm[i].value.f,
+						  MPFR_RNDN));
+			  for (size_t j = 0; j < tf->num_ret; j++)
+			    generic_value_free (&outputs_rm[j]);
+			}
 		    }
 		  break;