/* 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
{
/* 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;
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. */
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. */
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. */
/* 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,
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",
{
/* 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. */
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;
/* 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. */
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. */
/* 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++)
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));
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)
{
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
{
{
{ "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
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)
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);
}
/* 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:
{
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))
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);
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);
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");
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)
{
*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;
}
}
/* 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:
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 ();
}
}
/* 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)
{
switch (type)
{
case type_fp:
- suffix = fp_formats[format].suffix;
+ suffix = "";
break;
case type_int:
}
}
-/* 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;
}
}
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++)
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];
<= 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;
{
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++)
{
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)
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)
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);
}