]> 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 57053ad42091cf7cacbc016022369ac8d7a5f881..95e05635d3241df3c1329d180d837b8fa7d460bb 100644 (file)
@@ -1,5 +1,5 @@
 /* Generate expected output for libm tests with MPFR and MPC.
-   Copyright (C) 2013 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
 
    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:
 
-   gcc -std=gnu99 -O2 -Wall -Wextra gen-auto-libm-tests.c -lmpc -lmpfr -lgmp \
+   gcc -std=gnu11 -O2 -Wall -Wextra gen-auto-libm-tests.c -lmpc -lmpfr -lgmp \
      -o gen-auto-libm-tests
 
    (use of current MPC and MPFR versions recommended) and run it as:
 
-   gen-auto-libm-tests auto-libm-test-in auto-libm-test-out
+   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:
 
    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; "xfail" indicates the test is disabled as
-   expected to produce incorrect results.  Otherwise, test flags are
-   of the form "spurious-<exception>" and "missing-<exception>", for
-   any exception ("overflow", "underflow", "inexact", "invalid",
+   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
+   modes other than round-to-nearest.  Otherwise, test flags are of
+   the form "spurious-<exception>" and "missing-<exception>", for any
+   exception ("overflow", "underflow", "inexact", "invalid",
    "divbyzero"), "spurious-errno" and "missing-errno", to indicate
    when tests are expected to deviate from the exception and errno
    settings corresponding to the mathematical results.  "xfail",
-   "spurious-" and "missing-" flags should be accompanied by a comment
-   referring to an open bug in glibc Bugzilla.
+   "xfail-rounding", "spurious-" and "missing-" flags should be
+   accompanied by a comment referring to an open bug in glibc
+   Bugzilla.
 
-   The output file auto-libm-test-out contains the test lines from
+   The output file auto-libm-test-out-<func> contains the test lines from
    auto-libm-test-in, and, after the line for a given test, some
    number of output test lines.  An output test line is of the form "=
    function rounding-mode format input1 input2 ... : output1 output2
    ... : flags".  rounding-mode is "tonearest", "towardzero", "upward"
    or "downward".  format is a name from the floating_point_formats
    array, possibly followed by a sequence of ":flag" for flags from
-   "long32", "long64", "before-rounding" and "after-rounding" (the
-   last two indicating tests where expectations for underflow
-   exceptions depend on how the architecture detects tininess).
-   Inputs and outputs are specified as hex floats with the required
-   suffix for the floating-point type, or 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", "xfail", "<exception>",
-   "<exception>-ok", "errno-<value>", "errno-<value>-ok", where
-   "<exception>" and "errno-<value>" are unconditional, indicating
-   that a correct result means the given exception should be raised or
-   errno should be set to the given value, and other settings may be
-   conditional or unconditional; "-ok" means not to test for the given
+   "long32" and "long64".  Inputs and outputs are specified as hex
+   floats with the required suffix for the floating-point type, or
+   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
+   "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.
+   "errno-<value>" indicates that a correct result means errno should
+   be set to the given value.  "-ok" means not to test for the given
    exception or errno value (whether because it was marked as possibly
    missing or spurious, or because the calculation of correct results
-   indicated it was optional).  */
+   indicated it was optional).  Conditions "before-rounding" and
+   "after-rounding" indicate tests where expectations for underflow
+   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
 
@@ -154,9 +181,6 @@ typedef struct
 {
   /* The name of the format.  */
   const char *name;
-  /* The suffix to use on floating-point constants with this
-     format.  */
-  const char *suffix;
   /* A string for the largest normal value, or NULL for IEEE formats
      where this can be determined automatically.  */
   const char *max_string;
@@ -168,6 +192,8 @@ typedef struct
   int min_exp;
   /* The largest normal value.  */
   mpfr_t max;
+  /* The value 0.5ulp above the least positive normal value.  */
+  mpfr_t min_plus_half;
   /* The least positive normal value, 2^(MIN_EXP-1).  */
   mpfr_t min;
   /* The greatest positive subnormal value.  */
@@ -180,13 +206,13 @@ typedef struct
    enumeration.  */
 static fp_format_desc fp_formats[fp_num_formats] =
   {
-    { "flt-32", "f", NULL, 24, 128, -125, {}, {}, {}, {} },
-    { "dbl-64", "", NULL, 53, 1024, -1021, {}, {}, {}, {} },
-    { "ldbl-96-intel", "L", NULL, 64, 16384, -16381, {}, {}, {}, {} },
-    { "ldbl-96-m68k", "L", NULL, 64, 16384, -16382, {}, {}, {}, {} },
-    { "ldbl-128", "L", NULL, 113, 16384, -16381, {}, {}, {}, {} },
-    { "ldbl-128ibm", "L", "0x1.fffffffffffff7ffffffffffff8p+1023",
-      106, 1024, -968, {}, {}, {}, {} },
+    { "binary32", NULL, 24, 128, -125, {}, {}, {}, {}, {} },
+    { "binary64", NULL, 53, 1024, -1021, {}, {}, {}, {}, {} },
+    { "intel96", NULL, 64, 16384, -16381, {}, {}, {}, {}, {} },
+    { "m68k96", NULL, 64, 16384, -16382, {}, {}, {}, {}, {} },
+    { "binary128", NULL, 113, 16384, -16381, {}, {}, {}, {}, {} },
+    { "ibm128", "0x1.fffffffffffff7ffffffffffff8p+1023",
+      106, 1024, -968, {}, {}, {}, {}, {} },
   };
 
 /* The supported rounding modes.  */
@@ -207,16 +233,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.  */
@@ -307,8 +335,9 @@ typedef struct
 /* A type of input flag.  */
 typedef enum
   {
-    flag_no_test_inline,
+    flag_ignore_zero_inf_sign,
     flag_xfail,
+    flag_xfail_rounding,
     /* The "spurious" and "missing" flags must be in the same order as
        the fp_exception enumeration.  */
     flag_spurious_divbyzero,
@@ -333,8 +362,9 @@ typedef enum
    enumeration.  */
 static const char *const input_flags[num_input_flag_types] =
   {
-    "no-test-inline",
+    "ignore-zero-inf-sign",
     "xfail",
+    "xfail-rounding",
     "spurious-divbyzero",
     "spurious-inexact",
     "spurious-invalid",
@@ -387,6 +417,28 @@ typedef enum
   {
     /* MPFR function with a single argument and result.  */
     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,
+    /* MPFR function with integer and floating-point arguments and one
+       result.  */
+    mpfr_if_f,
+    /* MPFR function with a single argument and two floating-point
+       results.  */
+    mpfr_f_11,
+    /* MPC function with a single complex argument and one real
+       result.  */
+    mpc_c_f,
+    /* MPC function with a single complex argument and one complex
+       result.  */
+    mpc_c_c,
+    /* MPC function with two complex arguments and one complex
+       result.  */
+    mpc_cc_c,
   } func_calc_method;
 
 /* Description of how to calculate a function.  */
@@ -398,6 +450,15 @@ typedef struct
   union
   {
     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);
+    int (*mpc_c_f) (mpfr_t, const mpc_t, mpfr_rnd_t);
+    int (*mpc_c_c) (mpc_t, const mpc_t, mpc_rnd_t);
+    int (*mpc_cc_c) (mpc_t, const mpc_t, const mpc_t, mpc_rnd_t);
   } func;
 } func_calc_desc;
 
@@ -420,6 +481,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.  */
@@ -430,16 +495,102 @@ typedef struct
   input_test *tests;
 } test_function;
 
-#define FUNC_mpfr_f_f(NAME, MPFR_FUNC, EXACT)          \
-  {                                                    \
-    NAME, 1, { type_fp }, 1, { type_fp }, EXACT, false,        \
-    { mpfr_f_f, { .mpfr_f_f = MPFR_FUNC } }, 0, 0, NULL \
+#define ARGS1(T1) 1, { T1 }
+#define ARGS2(T1, T2) 2, { T1, T2 }
+#define ARGS3(T1, T2, T3) 3, { T1, T2, T3 }
+#define ARGS4(T1, T2, T3, T4) 4, { T1, T2, T3, T4 }
+#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, 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, 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,  \
+       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, \
+       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,   \
+       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, false, CALC (mpc_c_c, MPFR_FUNC))
+
 /* List of functions handled by this program.  */
 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),
+    FUNC_mpfr_ff_f ("atan2", mpfr_atan2, false),
+    FUNC_mpfr_f_f ("atanh", mpfr_atanh, false),
+    FUNC_mpc_c_f ("cabs", mpc_abs, false),
+    FUNC_mpc_c_c ("cacos", mpc_acos, false),
+    FUNC_mpc_c_c ("cacosh", mpc_acosh, false),
+    FUNC_mpc_c_f ("carg", mpc_arg, false),
+    FUNC_mpc_c_c ("casin", mpc_asin, false),
+    FUNC_mpc_c_c ("casinh", mpc_asinh, false),
+    FUNC_mpc_c_c ("catan", mpc_atan, false),
+    FUNC_mpc_c_c ("catanh", mpc_atanh, false),
+    FUNC_mpfr_f_f ("cbrt", mpfr_cbrt, false),
+    FUNC_mpc_c_c ("ccos", mpc_cos, false),
+    FUNC_mpc_c_c ("ccosh", mpc_cosh, false),
+    FUNC_mpc_c_c ("cexp", mpc_exp, false),
+    FUNC_mpc_c_c ("clog", mpc_log, false),
+    FUNC_mpc_c_c ("clog10", mpc_log10, false),
+    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, 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),
+    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),
+    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,
+         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),
+    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),
+    FUNC_mpfr_f_f ("tgamma", mpfr_gamma, false),
+    FUNC_mpfr_f_f ("y0", mpfr_y0, false),
+    FUNC_mpfr_f_f ("y1", mpfr_y1, false),
+    FUNC_mpfr_if_f ("yn", mpfr_yn, false),
   };
 
 /* Allocate memory, with error checking.  */
@@ -546,7 +697,7 @@ generic_value_copy (generic_value *dest, const generic_value *src)
 /* Initialize data for floating-point formats.  */
 
 static void
-init_fp_formats ()
+init_fp_formats (void)
 {
   int global_max_exp = 0, global_min_subnorm_exp = 0;
   for (fp_format f = fp_first_format; f < fp_num_formats; f++)
@@ -578,6 +729,10 @@ init_fp_formats ()
       assert_exact (mpfr_set_ui_2exp (fp_formats[f].min, 1,
                                      fp_formats[f].min_exp - 1,
                                      MPFR_RNDN));
+      mpfr_init2 (fp_formats[f].min_plus_half, fp_formats[f].mant_dig + 1);
+      assert_exact (mpfr_set (fp_formats[f].min_plus_half,
+                             fp_formats[f].min, MPFR_RNDN));
+      mpfr_nextabove (fp_formats[f].min_plus_half);
       mpfr_init2 (fp_formats[f].subnorm_max, fp_formats[f].mant_dig);
       assert_exact (mpfr_set (fp_formats[f].subnorm_max, fp_formats[f].min,
                              MPFR_RNDN));
@@ -615,6 +770,54 @@ special_fill_minus_max (mpfr_t res0, mpfr_t res1 __attribute__ ((unused)),
   return 1;
 }
 
+static size_t
+special_fill_min (mpfr_t res0, mpfr_t res1 __attribute__ ((unused)),
+                 fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set (res0, fp_formats[format].min, MPFR_RNDN));
+  return 1;
+}
+
+static size_t
+special_fill_minus_min (mpfr_t res0, mpfr_t res1 __attribute__ ((unused)),
+                       fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_neg (res0, fp_formats[format].min, MPFR_RNDN));
+  return 1;
+}
+
+static size_t
+special_fill_min_subnorm (mpfr_t res0, mpfr_t res1 __attribute__ ((unused)),
+                         fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set (res0, fp_formats[format].subnorm_min, MPFR_RNDN));
+  return 1;
+}
+
+static size_t
+special_fill_minus_min_subnorm (mpfr_t res0,
+                               mpfr_t res1 __attribute__ ((unused)),
+                               fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_neg (res0, fp_formats[format].subnorm_min, MPFR_RNDN));
+  return 1;
+}
+
+static size_t
+special_fill_min_subnorm_p120 (mpfr_t res0,
+                              mpfr_t res1 __attribute__ ((unused)),
+                              fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_mul_2ui (res0, fp_formats[format].subnorm_min,
+                             120, MPFR_RNDN));
+  return 1;
+}
+
 static size_t
 special_fill_pi (mpfr_t res0, mpfr_t res1, fp_format format)
 {
@@ -637,6 +840,140 @@ special_fill_minus_pi (mpfr_t res0, mpfr_t res1, fp_format format)
   return 2;
 }
 
+static size_t
+special_fill_pi_2 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  mpfr_const_pi (res0, MPFR_RNDU);
+  assert_exact (mpfr_div_ui (res0, res0, 2, MPFR_RNDN));
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  mpfr_const_pi (res1, MPFR_RNDD);
+  assert_exact (mpfr_div_ui (res1, res1, 2, MPFR_RNDN));
+  return 2;
+}
+
+static size_t
+special_fill_minus_pi_2 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  mpfr_const_pi (res0, MPFR_RNDU);
+  assert_exact (mpfr_div_ui (res0, res0, 2, MPFR_RNDN));
+  assert_exact (mpfr_neg (res0, res0, MPFR_RNDN));
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  mpfr_const_pi (res1, MPFR_RNDD);
+  assert_exact (mpfr_div_ui (res1, res1, 2, MPFR_RNDN));
+  assert_exact (mpfr_neg (res1, res1, MPFR_RNDN));
+  return 2;
+}
+
+static size_t
+special_fill_pi_4 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res0, 1, MPFR_RNDN));
+  mpfr_atan (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res1, 1, MPFR_RNDN));
+  mpfr_atan (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
+static size_t
+special_fill_pi_6 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res0, 1, -1, MPFR_RNDN));
+  mpfr_asin (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res1, 1, -1, MPFR_RNDN));
+  mpfr_asin (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
+static size_t
+special_fill_minus_pi_6 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res0, -1, -1, MPFR_RNDN));
+  mpfr_asin (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res1, -1, -1, MPFR_RNDN));
+  mpfr_asin (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
+static size_t
+special_fill_pi_3 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res0, 1, -1, MPFR_RNDN));
+  mpfr_acos (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res1, 1, -1, MPFR_RNDN));
+  mpfr_acos (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
+static size_t
+special_fill_2pi_3 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res0, -1, -1, MPFR_RNDN));
+  mpfr_acos (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si_2exp (res1, -1, -1, MPFR_RNDN));
+  mpfr_acos (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
+static size_t
+special_fill_2pi (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  mpfr_const_pi (res0, MPFR_RNDU);
+  assert_exact (mpfr_mul_ui (res0, res0, 2, MPFR_RNDN));
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  mpfr_const_pi (res1, MPFR_RNDD);
+  assert_exact (mpfr_mul_ui (res1, res1, 2, MPFR_RNDN));
+  return 2;
+}
+
+static size_t
+special_fill_e (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res0, 1, MPFR_RNDN));
+  mpfr_exp (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res1, 1, MPFR_RNDN));
+  mpfr_exp (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
+static size_t
+special_fill_1_e (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res0, -1, MPFR_RNDN));
+  mpfr_exp (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res1, -1, MPFR_RNDN));
+  mpfr_exp (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
+static size_t
+special_fill_e_minus_1 (mpfr_t res0, mpfr_t res1, fp_format format)
+{
+  mpfr_init2 (res0, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res0, 1, MPFR_RNDN));
+  mpfr_expm1 (res0, res0, MPFR_RNDU);
+  mpfr_init2 (res1, fp_formats[format].mant_dig);
+  assert_exact (mpfr_set_si (res1, 1, MPFR_RNDN));
+  mpfr_expm1 (res1, res1, MPFR_RNDD);
+  return 2;
+}
+
 /* A special string accepted in input arguments.  */
 typedef struct
 {
@@ -654,8 +991,24 @@ static const special_real_input special_real_inputs[] =
   {
     { "max", special_fill_max },
     { "-max", special_fill_minus_max },
+    { "min", special_fill_min },
+    { "-min", special_fill_minus_min },
+    { "min_subnorm", special_fill_min_subnorm },
+    { "-min_subnorm", special_fill_minus_min_subnorm },
+    { "min_subnorm_p120", special_fill_min_subnorm_p120 },
     { "pi", special_fill_pi },
     { "-pi", special_fill_minus_pi },
+    { "pi/2", special_fill_pi_2 },
+    { "-pi/2", special_fill_minus_pi_2 },
+    { "pi/4", special_fill_pi_4 },
+    { "pi/6", special_fill_pi_6 },
+    { "-pi/6", special_fill_minus_pi_6 },
+    { "pi/3", special_fill_pi_3 },
+    { "2pi/3", special_fill_2pi_3 },
+    { "2pi", special_fill_2pi },
+    { "e", special_fill_e },
+    { "1/e", special_fill_1_e },
+    { "e-1", special_fill_e_minus_1 },
   };
 
 /* Given a real number R computed in round-to-zero mode, set the
@@ -668,7 +1021,7 @@ adjust_real (mpfr_t r, bool inexact)
   if (!inexact)
     return;
   /* NaNs are exact, as are infinities in round-to-zero mode.  */
-  assert (mpfr_regular_p (r));
+  assert (mpfr_number_p (r));
   if (mpfr_cmpabs (r, global_min) < 0)
     assert_exact (mpfr_copysign (r, global_min, r, MPFR_RNDN));
   else if (mpfr_cmpabs (r, global_max) > 0)
@@ -678,7 +1031,14 @@ adjust_real (mpfr_t r, bool inexact)
       mpz_t tmp;
       mpz_init (tmp);
       mpfr_exp_t e = mpfr_get_z_2exp (tmp, r);
-      mpz_setbit (tmp, 0);
+      if (mpz_sgn (tmp) < 0)
+       {
+         mpz_neg (tmp, tmp);
+         mpz_setbit (tmp, 0);
+         mpz_neg (tmp, tmp);
+       }
+      else
+       mpz_setbit (tmp, 0);
       assert_exact (mpfr_set_z_2exp (r, tmp, e, MPFR_RNDN));
       mpz_clear (tmp);
     }
@@ -776,16 +1136,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:
@@ -809,6 +1172,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))
@@ -820,7 +1185,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);
@@ -828,7 +1195,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);
@@ -879,6 +1246,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");
@@ -924,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)
        {
@@ -1003,7 +1374,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;
            }
@@ -1067,13 +1438,20 @@ 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)
     {
     case mpfr_f_f:
@@ -1081,10 +1459,160 @@ 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;
 
+    case mpfr_ff_f:
+      assert (inputs[0].type == gtype_fp);
+      assert (inputs[1].type == gtype_fp);
+      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, 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;
+
+    case mpfr_f_f1:
+      assert (inputs[0].type == gtype_fp);
+      outputs[0].type = gtype_fp;
+      outputs[1].type = gtype_int;
+      mpfr_init (outputs[0].value.f);
+      int i = 0;
+      inexact = calc->func.mpfr_f_f1 (outputs[0].value.f, &i,
+                                     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;
+
+    case mpfr_if_f:
+      assert (inputs[0].type == gtype_int);
+      assert (inputs[1].type == gtype_fp);
+      outputs[0].type = gtype_fp;
+      mpfr_init (outputs[0].value.f);
+      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, 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_f_11:
+      assert (inputs[0].type == gtype_fp);
+      outputs[0].type = gtype_fp;
+      mpfr_init (outputs[0].value.f);
+      outputs[1].type = gtype_fp;
+      mpfr_init (outputs[1].value.f);
+      int comb_ternary = calc->func.mpfr_f_11 (outputs[0].value.f,
+                                              outputs[1].value.f,
+                                              inputs[0].value.f,
+                                              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;
+
+    case mpc_c_f:
+      assert (inputs[0].type == gtype_fp);
+      assert (inputs[1].type == gtype_fp);
+      outputs[0].type = gtype_fp;
+      mpfr_init (outputs[0].value.f);
+      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, 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;
+
+    case mpc_c_c:
+      assert (inputs[0].type == gtype_fp);
+      assert (inputs[1].type == gtype_fp);
+      outputs[0].type = gtype_fp;
+      mpfr_init (outputs[0].value.f);
+      outputs[1].type = gtype_fp;
+      mpfr_init (outputs[1].value.f);
+      mpc_init2 (ci1, internal_precision);
+      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, 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),
+                             MPFR_RNDN));
+      adjust_real (outputs[0].value.f, MPC_INEX_RE (mpc_ternary));
+      adjust_real (outputs[1].value.f, MPC_INEX_IM (mpc_ternary));
+      mpc_clear (ci1);
+      mpc_clear (co);
+      break;
+
+    case mpc_cc_c:
+      assert (inputs[0].type == gtype_fp);
+      assert (inputs[1].type == gtype_fp);
+      assert (inputs[2].type == gtype_fp);
+      assert (inputs[3].type == gtype_fp);
+      outputs[0].type = gtype_fp;
+      mpfr_init (outputs[0].value.f);
+      outputs[1].type = gtype_fp;
+      mpfr_init (outputs[1].value.f);
+      mpc_init2 (ci1, internal_precision);
+      mpc_init2 (ci2, internal_precision);
+      mpc_init2 (co, internal_precision);
+      assert_exact (mpc_set_fr_fr (ci1, inputs[0].value.f, inputs[1].value.f,
+                                  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, 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),
+                             MPFR_RNDN));
+      adjust_real (outputs[0].value.f, MPC_INEX_RE (mpc_ternary));
+      adjust_real (outputs[1].value.f, MPC_INEX_IM (mpc_ternary));
+      mpc_clear (ci1);
+      mpc_clear (ci2);
+      mpc_clear (co);
+      break;
+
     default:
       abort ();
     }
@@ -1137,13 +1665,12 @@ int_fits_type (mpz_t z, arg_ret_type type, int long_bits)
 }
 
 /* Print a generic value V to FP (name FILENAME), preceded by a space,
-   for type TYPE, floating-point format FORMAT, LONG_BITS bits per
-   long, printing " IGNORE" instead if IGNORE.  */
+   for type TYPE, LONG_BITS bits per long, printing " IGNORE" instead
+   if IGNORE.  */
 
 static void
 output_generic_value (FILE *fp, const char *filename, const generic_value *v,
-                     bool ignore, arg_ret_type type, fp_format format,
-                     int long_bits)
+                     bool ignore, arg_ret_type type, int long_bits)
 {
   if (ignore)
     {
@@ -1156,7 +1683,7 @@ output_generic_value (FILE *fp, const char *filename, const generic_value *v,
   switch (type)
     {
     case type_fp:
-      suffix = fp_formats[format].suffix;
+      suffix = "";
       break;
 
     case type_int:
@@ -1216,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;
@@ -1244,7 +1772,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++)
@@ -1292,21 +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];
@@ -1341,8 +1929,24 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf,
                                    <= 0));
                          may_underflow
                            |= (!mpfr_zero_p (generic_outputs[i].value.f)
-                               && mpfr_cmpabs (generic_outputs[i].value.f,
-                                               fp_formats[f].min) <= 0);
+                               && (mpfr_cmpabs (generic_outputs[i].value.f,
+                                                fp_formats[f].min_plus_half)
+                                   <= 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;
@@ -1367,213 +1971,272 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf,
            {
              bool before_after_matters
                = tf->exact && merged_exc_before[m] != merged_exc_after[m];
-             for (int after = 0; after <= 1; after++)
+             if (before_after_matters)
                {
-                 if (after == 1 && !before_after_matters)
-                   continue;
-                 const char *after_cond;
-                 if (before_after_matters)
-                   after_cond = (after
-                                 ? ":after-rounding"
-                                 : ":before-rounding");
-                 else
-                   after_cond = "";
-                 unsigned int merged_exc = (after
-                                            ? merged_exc_after[m]
-                                            : merged_exc_before[m]);
-                 if (fprintf (fp, "= %s %s %s%s%s", tf->name,
-                              rounding_modes[m].name, fp_formats[f].name,
-                              long_cond, after_cond) < 0)
+                 assert ((merged_exc_before[m] ^ merged_exc_after[m])
+                         == (1U << exc_underflow));
+                 assert ((merged_exc_before[m] & (1U << exc_underflow)) != 0);
+               }
+             unsigned int merged_exc = merged_exc_before[m];
+             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);
-                 /* Print inputs.  */
-                 for (size_t i = 0; i < tf->num_args; i++)
-                   output_generic_value (fp, filename, &inputs[i], false,
-                                         tf->arg_types[i], f, long_bits);
-                 if (fputs (" :", fp) < 0)
+               }
+             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 outputs.  */
-                 bool must_erange = false;
-                 for (size_t i = 0; i < tf->num_ret; i++)
+               }
+             /* Print inputs.  */
+             for (size_t i = 0; i < tf->num_args; i++)
+               output_generic_value (fp, filename, &inputs[i], false,
+                                     tf->arg_types[i], long_bits);
+             if (fputs (" :", fp) < 0)
+               error (EXIT_FAILURE, errno, "write to '%s'", filename);
+             /* Print outputs.  */
+             bool must_erange = false;
+             bool some_underflow_zero = false;
+             for (size_t i = 0; i < tf->num_ret; i++)
+               {
+                 generic_value g;
+                 g.type = generic_outputs[i].type;
+                 switch (g.type)
                    {
-                     generic_value g;
-                     g.type = generic_outputs[i].type;
-                     switch (g.type)
-                       {
-                       case gtype_fp:
-                         if (mpfr_inf_p (all_res[i][m])
-                             && (all_exc_before[i][m]
-                                 & (1U << exc_overflow)) != 0)
-                           must_erange = true;
-                         if (mpfr_zero_p (all_res[i][m])
-                             && (all_exc_before[i][m]
-                                 & (1U << exc_underflow)) != 0)
-                           must_erange = true;
-                         mpfr_init2 (g.value.f, fp_formats[f].mant_dig);
-                         assert_exact (mpfr_set (g.value.f, all_res[i][m],
-                                                 MPFR_RNDN));
-                         break;
+                   case gtype_fp:
+                     if (mpfr_inf_p (all_res[i][m])
+                         && (all_exc_before[i][m]
+                             & (1U << exc_overflow)) != 0)
+                       must_erange = true;
+                     if (mpfr_zero_p (all_res[i][m])
+                         && (tf->exact
+                             || mpfr_zero_p (all_res[i][rm_tonearest]))
+                         && (all_exc_before[i][m]
+                             & (1U << exc_underflow)) != 0)
+                       must_erange = true;
+                     if (mpfr_zero_p (all_res[i][rm_towardzero])
+                         && (all_exc_before[i][m]
+                             & (1U << exc_underflow)) != 0)
+                       some_underflow_zero = true;
+                     mpfr_init2 (g.value.f, fp_formats[f].mant_dig);
+                     assert_exact (mpfr_set (g.value.f, all_res[i][m],
+                                             MPFR_RNDN));
+                     break;
 
-                       case gtype_int:
-                         mpz_init (g.value.i);
-                         mpz_set (g.value.i, generic_outputs[i].value.i);
-                         break;
+                   case gtype_int:
+                     mpz_init (g.value.i);
+                     mpz_set (g.value.i, generic_outputs[i].value.i);
+                     break;
 
-                       default:
-                         abort ();
-                       }
-                     output_generic_value (fp, filename, &g, ignore_output[i],
-                                           tf->ret_types[i], f, long_bits);
-                     generic_value_free (&g);
+                   default:
+                     abort ();
                    }
-                 if (fputs (" :", fp) < 0)
-                   error (EXIT_FAILURE, errno, "write to '%s'", filename);
-                 /* Print miscellaneous flags (passed through from
-                    input).  */
-                 for (size_t i = 0; i < it->num_flags; i++)
-                   switch (it->flags[i].type)
-                     {
-                     case flag_no_test_inline:
-                     case flag_xfail:
-                       if (fprintf (fp, " %s%s",
-                                    input_flags[it->flags[i].type],
-                                    (it->flags[i].cond
-                                     ? it->flags[i].cond
-                                     : "")) < 0)
+                 output_generic_value (fp, filename, &g, ignore_output[i],
+                                       tf->ret_types[i], long_bits);
+                 generic_value_free (&g);
+               }
+             if (fputs (" :", fp) < 0)
+               error (EXIT_FAILURE, errno, "write to '%s'", filename);
+             /* Print miscellaneous flags (passed through from
+                input).  */
+             for (size_t i = 0; i < it->num_flags; i++)
+               switch (it->flags[i].type)
+                 {
+                 case flag_ignore_zero_inf_sign:
+                 case flag_xfail:
+                   if (fprintf (fp, " %s%s",
+                                input_flags[it->flags[i].type],
+                                (it->flags[i].cond
+                                 ? it->flags[i].cond
+                                 : "")) < 0)
+                     error (EXIT_FAILURE, errno, "write to '%s'",
+                            filename);
+                   break;
+                 case flag_xfail_rounding:
+                   if (m != rm_tonearest)
+                     if (fprintf (fp, " xfail%s",
+                                  (it->flags[i].cond
+                                   ? it->flags[i].cond
+                                   : "")) < 0)
+                       error (EXIT_FAILURE, errno, "write to '%s'",
+                              filename);
+                   break;
+                 default:
+                   break;
+                 }
+             /* For the ibm128 format, expect incorrect overflowing
+                results in rounding modes other than to nearest;
+                likewise incorrect results where the result may
+                underflow to 0.  */
+             if (f == fp_ldbl_128ibm
+                 && m != rm_tonearest
+                 && (some_underflow_zero
+                     || (merged_exc_before[m] & (1U << exc_overflow)) != 0))
+               if (fputs (" xfail:ibm128-libgcc", fp) < 0)
+                 error (EXIT_FAILURE, errno, "write to '%s'", filename);
+             /* Print exception flags and compute errno
+                expectations where not already computed.  */
+             bool may_edom = false;
+             bool must_edom = false;
+             bool may_erange = must_erange || may_underflow;
+             for (fp_exception e = exc_first_exception;
+                  e < exc_num_exceptions;
+                  e++)
+               {
+                 bool expect_e = (merged_exc & (1U << e)) != 0;
+                 bool e_optional = false;
+                 switch (e)
+                   {
+                   case exc_divbyzero:
+                     if (expect_e)
+                       may_erange = must_erange = true;
+                     break;
+
+                   case exc_inexact:
+                     if (!tf->exact)
+                       e_optional = true;
+                     break;
+
+                   case exc_invalid:
+                     if (expect_e)
+                       may_edom = must_edom = true;
+                     break;
+
+                   case exc_overflow:
+                     if (expect_e)
+                       may_erange = true;
+                     break;
+
+                   case exc_underflow:
+                     if (expect_e)
+                       may_erange = true;
+                     if (must_underflow)
+                       assert (expect_e);
+                     if (may_underflow && !must_underflow)
+                       e_optional = true;
+                     break;
+
+                   default:
+                     abort ();
+                   }
+                 if (e_optional)
+                   {
+                     assert (!before_after_matters);
+                     if (fprintf (fp, " %s-ok", exceptions[e]) < 0)
+                       error (EXIT_FAILURE, errno, "write to '%s'",
+                              filename);
+                   }
+                 else
+                   {
+                     if (expect_e)
+                       if (fprintf (fp, " %s", exceptions[e]) < 0)
                          error (EXIT_FAILURE, errno, "write to '%s'",
                                 filename);
-                       break;
-                     default:
-                       break;
-                     }
-                 /* Print exception flags and compute errno
-                    expectations where not already computed.  */
-                 bool may_edom = false;
-                 bool must_edom = false;
-                 bool may_erange = must_erange || may_underflow;
-                 for (fp_exception e = exc_first_exception;
-                      e < exc_num_exceptions;
-                      e++)
-                   {
-                     bool expect_e = (merged_exc & (1U << e)) != 0;
-                     bool e_optional = false;
-                     switch (e)
-                       {
-                       case exc_divbyzero:
-                         if (expect_e)
-                           may_erange = must_erange = true;
-                         break;
-
-                       case exc_inexact:
-                         if (!tf->exact)
-                           e_optional = true;
-                         break;
-
-                       case exc_invalid:
-                         if (expect_e)
-                           may_edom = must_edom = true;
-                         break;
-
-                       case exc_overflow:
-                         if (expect_e)
-                           may_erange = true;
-                         break;
-
-                       case exc_underflow:
-                         if (expect_e)
-                           may_erange = true;
-                         if (must_underflow)
-                           assert (expect_e);
-                         if (may_underflow && !must_underflow)
-                           e_optional = true;
-                         break;
-
-                       default:
-                         abort ();
-                       }
-                     if (e_optional)
-                       {
-                         if (fprintf (fp, " %s-ok", exceptions[e]) < 0)
-                           error (EXIT_FAILURE, errno, "write to '%s'",
-                                  filename);
-                       }
-                     else
+                     if (before_after_matters && e == exc_underflow)
+                       if (fputs (":before-rounding", fp) < 0)
+                         error (EXIT_FAILURE, errno, "write to '%s'",
+                                filename);
+                     for (int after = 0; after <= 1; after++)
                        {
-                         if (expect_e)
-                           if (fprintf (fp, " %s", exceptions[e]) < 0)
-                             error (EXIT_FAILURE, errno, "write to '%s'",
-                                    filename);
+                         bool expect_e_here = expect_e;
+                         if (after == 1 && (!before_after_matters
+                                            || e != exc_underflow))
+                           continue;
+                         const char *after_cond;
+                         if (before_after_matters && e == exc_underflow)
+                           {
+                             after_cond = (after
+                                           ? ":after-rounding"
+                                           : ":before-rounding");
+                             expect_e_here = !after;
+                           }
+                         else
+                           after_cond = "";
                          input_flag_type okflag;
-                         okflag = (expect_e
+                         okflag = (expect_e_here
                                    ? flag_missing_first
                                    : flag_spurious_first) + e;
                          for (size_t i = 0; i < it->num_flags; i++)
                            if (it->flags[i].type == okflag)
-                             if (fprintf (fp, " %s-ok%s",
+                             if (fprintf (fp, " %s-ok%s%s",
                                           exceptions[e],
                                           (it->flags[i].cond
                                            ? it->flags[i].cond
-                                           : "")) < 0)
+                                           : ""), after_cond) < 0)
                                error (EXIT_FAILURE, errno, "write to '%s'",
                                       filename);
                        }
                    }
-                 /* Print errno expectations.  */
-                 if (tf->complex_fn)
-                   {
-                     must_edom = false;
-                     must_erange = false;
-                   }
-                 if (may_edom && !must_edom)
-                   {
-                     if (fputs (" errno-edom-ok", fp) < 0)
+               }
+             /* Print errno expectations.  */
+             if (tf->complex_fn)
+               {
+                 must_edom = false;
+                 must_erange = false;
+               }
+             if (may_edom && !must_edom)
+               {
+                 if (fputs (" errno-edom-ok", fp) < 0)
+                   error (EXIT_FAILURE, errno, "write to '%s'",
+                          filename);
+               }
+             else
+               {
+                 if (must_edom)
+                   if (fputs (" errno-edom", fp) < 0)
+                     error (EXIT_FAILURE, errno, "write to '%s'",
+                            filename);
+                 input_flag_type okflag = (must_edom
+                                           ? flag_missing_errno
+                                           : flag_spurious_errno);
+                 for (size_t i = 0; i < it->num_flags; i++)
+                   if (it->flags[i].type == okflag)
+                     if (fprintf (fp, " errno-edom-ok%s",
+                                  (it->flags[i].cond
+                                   ? it->flags[i].cond
+                                   : "")) < 0)
                        error (EXIT_FAILURE, errno, "write to '%s'",
                               filename);
-                   }
-                 else
-                   {
-                     if (must_edom)
-                       if (fputs (" errno-edom", fp) < 0)
-                         error (EXIT_FAILURE, errno, "write to '%s'",
-                                filename);
-                     input_flag_type okflag = (must_edom
-                                               ? flag_missing_errno
-                                               : flag_spurious_errno);
-                     for (size_t i = 0; i < it->num_flags; i++)
-                       if (it->flags[i].type == okflag)
-                         if (fprintf (fp, " errno-edom-ok%s",
-                                      (it->flags[i].cond
-                                       ? it->flags[i].cond
-                                       : "")) < 0)
-                           error (EXIT_FAILURE, errno, "write to '%s'",
-                                  filename);
-                   }
-                 if (may_erange && !must_erange)
-                   {
-                     if (fputs (" errno-erange-ok", fp) < 0)
+               }
+             if (before_after_matters)
+               assert (may_erange && !must_erange);
+             if (may_erange && !must_erange)
+               {
+                 if (fprintf (fp, " errno-erange-ok%s",
+                              (before_after_matters
+                               ? ":before-rounding"
+                               : "")) < 0)
+                   error (EXIT_FAILURE, errno, "write to '%s'",
+                          filename);
+               }
+             if (before_after_matters || !(may_erange && !must_erange))
+               {
+                 if (must_erange)
+                   if (fputs (" errno-erange", fp) < 0)
+                     error (EXIT_FAILURE, errno, "write to '%s'",
+                            filename);
+                 input_flag_type okflag = (must_erange
+                                           ? flag_missing_errno
+                                           : flag_spurious_errno);
+                 for (size_t i = 0; i < it->num_flags; i++)
+                   if (it->flags[i].type == okflag)
+                     if (fprintf (fp, " errno-erange-ok%s%s",
+                                  (it->flags[i].cond
+                                   ? it->flags[i].cond
+                                   : ""),
+                                  (before_after_matters
+                                   ? ":after-rounding"
+                                   : "")) < 0)
                        error (EXIT_FAILURE, errno, "write to '%s'",
                               filename);
-                   }
-                 else
-                   {
-                     if (must_erange)
-                       if (fputs (" errno-erange", fp) < 0)
-                         error (EXIT_FAILURE, errno, "write to '%s'",
-                                filename);
-                     input_flag_type okflag = (must_erange
-                                               ? flag_missing_errno
-                                               : flag_spurious_errno);
-                     for (size_t i = 0; i < it->num_flags; i++)
-                       if (it->flags[i].type == okflag)
-                         if (fprintf (fp, " errno-erange-ok%s",
-                                      (it->flags[i].cond
-                                       ? it->flags[i].cond
-                                       : "")) < 0)
-                           error (EXIT_FAILURE, errno, "write to '%s'",
-                                  filename);
-                   }
-                 if (putc ('\n', fp) < 0)
-                   error (EXIT_FAILURE, errno, "write to '%s'", filename);
                }
+             if (putc ('\n', fp) < 0)
+               error (EXIT_FAILURE, errno, "write to '%s'", filename);
            }
          for (size_t i = 0; i < tf->num_ret; i++)
            {
@@ -1588,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 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 *filename)
+generate_output (const char *function, bool narrow, const char *filename)
 {
   FILE *fp = fopen (filename, "w");
   if (fp == NULL)
@@ -1599,13 +2264,16 @@ generate_output (const char *filename)
   for (size_t i = 0; i < ARRAY_SIZE (test_functions); i++)
     {
       test_function *tf = &test_functions[i];
+      if (strcmp (tf->name, function) != 0)
+       continue;
       for (size_t j = 0; j < tf->num_tests; j++)
        {
          input_test *it = &tf->tests[j];
          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)
@@ -1615,12 +2283,30 @@ generate_output (const char *filename)
 int
 main (int argc, char **argv)
 {
-  if (argc != 3)
-    error (EXIT_FAILURE, 0, "usage: gen-auto-libm-tests <input> <output>");
+  if (argc != 4
+      && !(argc == 5 && strcmp (argv[1], "--narrow") == 0))
+    error (EXIT_FAILURE, 0,
+          "usage: gen-auto-libm-tests [--narrow] <input> <func> <output>");
+  bool narrow;
   const char *input_filename = argv[1];
-  const char *output_filename = argv[2];
+  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 (output_filename);
+  generate_output (function, narrow, output_filename);
   exit (EXIT_SUCCESS);
 }