Commit 5b4f088d by Martin Sebor

PR tree-optimization/78696 - [7 Regression] -fprintf-return-value misoptimizes…

PR tree-optimization/78696 - [7 Regression] -fprintf-return-value misoptimizes %.Ng where N is greater than 10

gcc/ChangeLog:

	PR tree-optimization/78696
	* gimple-ssa-sprintf.c (format_floating): Correct handling of
	precision.  Use MPFR for %f for greater fidelity.  Correct handling
	of %g.
	(pass_sprintf_length::compute_format_length): Set width and precision
	specified by asrerisk to void_node for vararg functions.
	(try_substitute_return_value): Adjust dump output.

gcc/testsuite/ChangeLog:

	PR tree-optimization/78696
	* gcc.dg/tree-ssa/builtin-sprintf-5.c: Remove incorrect test cases.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: Correct off-by-1 errors.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.

From-SVN: r244037
parent 0f2a6e84
2017-01-03 Martin Sebor <msebor@redhat.com>
PR tree-optimization/78696
* gimple-ssa-sprintf.c (format_floating): Correct handling of
precision. Use MPFR for %f for greater fidelity. Correct handling
of %g.
(pass_sprintf_length::compute_format_length): Set width and precision
specified by asrerisk to void_node for vararg functions.
(try_substitute_return_value): Adjust dump output.
2017-01-03 David Edelsohn <dje.gcc@gmail.com> 2017-01-03 David Edelsohn <dje.gcc@gmail.com>
* doc/invoke.texi (RS6000 options): LRA is enabled by default. * doc/invoke.texi (RS6000 options): LRA is enabled by default.
......
...@@ -483,9 +483,11 @@ struct conversion_spec ...@@ -483,9 +483,11 @@ struct conversion_spec
/* Numeric precision as in "%.32s". */ /* Numeric precision as in "%.32s". */
int precision; int precision;
/* Width specified via the '*' character. */ /* Width specified via the '*' character. Need not be INTEGER_CST.
For vararg functions set to void_node. */
tree star_width; tree star_width;
/* Precision specified via the asterisk. */ /* Precision specified via the asterisk. Need not be INTEGER_CST.
For vararg functions set to void_node. */
tree star_precision; tree star_precision;
/* Length modifier. */ /* Length modifier. */
...@@ -1246,15 +1248,26 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec, ...@@ -1246,15 +1248,26 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec,
fmtstr[len + 5] = spec; fmtstr[len + 5] = spec;
fmtstr[len + 6] = '\0'; fmtstr[len + 6] = '\0';
spec = TOUPPER (spec);
if (spec == 'E' || spec == 'F')
{
/* For %e, specify the precision explicitly since mpfr_sprintf
does its own thing just to be different (see MPFR bug 21088). */
if (prec < 0)
prec = 6;
}
else
{
/* Avoid passing negative precisions with larger magnitude to MPFR /* Avoid passing negative precisions with larger magnitude to MPFR
to avoid exposing its bugs. (A negative precision is supposed to avoid exposing its bugs. (A negative precision is supposed
to be ignored.) */ to be ignored.) */
if (prec < 0) if (prec < 0)
prec = -1; prec = -1;
}
HOST_WIDE_INT p = prec; HOST_WIDE_INT p = prec;
if (TOUPPER (spec) == 'G') if (spec == 'G')
{ {
/* For G/g, precision gives the maximum number of significant /* For G/g, precision gives the maximum number of significant
digits which is bounded by LDBL_MAX_10_EXP, or, for a 128 digits which is bounded by LDBL_MAX_10_EXP, or, for a 128
...@@ -1287,7 +1300,8 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec, ...@@ -1287,7 +1300,8 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec,
} }
/* Return the number of bytes to format using the format specifier /* Return the number of bytes to format using the format specifier
SPEC the largest value in the real floating TYPE. */ SPEC and the precision PREC the largest value in the real floating
TYPE. */
static unsigned HOST_WIDE_INT static unsigned HOST_WIDE_INT
format_floating_max (tree type, char spec, HOST_WIDE_INT prec) format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
...@@ -1329,7 +1343,6 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width, ...@@ -1329,7 +1343,6 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
HOST_WIDE_INT prec) HOST_WIDE_INT prec)
{ {
tree type; tree type;
bool ldbl = false;
switch (spec.modifier) switch (spec.modifier)
{ {
...@@ -1340,12 +1353,10 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width, ...@@ -1340,12 +1353,10 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
case FMT_LEN_L: case FMT_LEN_L:
type = long_double_type_node; type = long_double_type_node;
ldbl = true;
break; break;
case FMT_LEN_ll: case FMT_LEN_ll:
type = long_double_type_node; type = long_double_type_node;
ldbl = true;
break; break;
default: default:
...@@ -1355,95 +1366,94 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width, ...@@ -1355,95 +1366,94 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
/* The minimum and maximum number of bytes produced by the directive. */ /* The minimum and maximum number of bytes produced by the directive. */
fmtresult res; fmtresult res;
/* Log10 of of the maximum number of exponent digits for the type. */ /* The result is always bounded (though the range may be all of int). */
int logexpdigs = 2; res.bounded = true;
if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2)
{
/* The base in which the exponent is represented should always
be 2 in GCC. */
const double log10_2 = .30102999566398119521; /* The minimum output as determined by flags. It's always at least 1. */
int flagmin = (1 /* for the first digit */
+ (spec.get_flag ('+') | spec.get_flag (' '))
+ (prec == 0 && spec.get_flag ('#')));
/* Compute T_MAX_EXP for base 2. */ if (width == INT_MIN || prec == INT_MIN)
int expdigs = REAL_MODE_FORMAT (TYPE_MODE (type))->emax * log10_2; {
logexpdigs = ilog (expdigs, 10); /* When either width or precision is specified but unknown
the upper bound is the maximum. Otherwise it will be
computed for each directive below. */
res.range.max = HOST_WIDE_INT_MAX;
} }
else
res.range.max = HOST_WIDE_INT_M1U;
switch (spec.specifier) switch (spec.specifier)
{ {
case 'A': case 'A':
case 'a': case 'a':
{ {
/* The minimum output is "0x.p+0". */ res.range.min = flagmin + 5 + (prec > 0 ? prec + 1 : 0);
res.range.min = 6 + (prec > 0 ? prec : 0); if (res.range.max == HOST_WIDE_INT_M1U)
res.range.max = (width == INT_MIN {
? HOST_WIDE_INT_MAX /* Compute the upper bound for -TYPE_MAX. */
: format_floating_max (type, 'a', prec)); res.range.max = format_floating_max (type, 'a', prec);
}
/* The output of "%a" is fully specified only when precision
is explicitly specified and width isn't unknown. */
res.bounded = INT_MIN != width && -1 < prec;
break; break;
} }
case 'E': case 'E':
case 'e': case 'e':
{ {
bool sign = spec.get_flag ('+') || spec.get_flag (' ');
/* The minimum output is "[-+]1.234567e+00" regardless /* The minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */ of the value of the actual argument. */
res.range.min = (sign res.range.min = (flagmin
+ 1 /* unit */ + (prec < 0 ? 7 : prec ? prec + 1 : 0) + (prec == INT_MIN
? 0 : prec < 0 ? 7 : prec ? prec + 1 : 0)
+ 2 /* e+ */ + 2); + 2 /* e+ */ + 2);
/* Unless width is uknown the maximum output is the minimum plus
sign (unless already included), plus the difference between if (res.range.max == HOST_WIDE_INT_M1U)
the minimum exponent of 2 and the maximum exponent for the type. */ {
res.range.max = (width == INT_MIN /* MPFR uses a precision of 16 by default for some reason.
? HOST_WIDE_INT_M1U Set it to the C default of 6. */
: res.range.min + !sign + logexpdigs - 2); res.range.max = format_floating_max (type, 'e',
-1 == prec ? 6 : prec);
/* "%e" is fully specified and the range of bytes is bounded }
unless width is unknown. */
res.bounded = INT_MIN != width;
break; break;
} }
case 'F': case 'F':
case 'f': case 'f':
{ {
/* The minimum output is "1.234567" regardless of the value /* The lower bound when precision isn't specified is 8 bytes
of the actual argument. */ ("1.23456" since precision is taken to be 6). When precision
res.range.min = 2 + (prec < 0 ? 6 : prec); is zero, the lower bound is 1 byte (e.g., "1"). Otherwise,
when precision is greater than zero, then the lower bound
is 2 plus precision (plus flags). */
res.range.min = (flagmin
+ (prec != INT_MIN) /* for decimal point */
+ (prec == INT_MIN
? 0 : prec < 0 ? 6 : prec ? prec : -1));
/* Compute the maximum just once. */ if (res.range.max == HOST_WIDE_INT_M1U)
const HOST_WIDE_INT f_max[] = { {
format_floating_max (double_type_node, 'f', prec), /* Compute the upper bound for -TYPE_MAX. */
format_floating_max (long_double_type_node, 'f', prec) res.range.max = format_floating_max (type, 'f', prec);
}; }
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl];
/* "%f" is fully specified and the range of bytes is bounded
unless width is unknown. */
res.bounded = INT_MIN != width;
break; break;
} }
case 'G': case 'G':
case 'g': case 'g':
{ {
/* The minimum is the same as for '%F'. */ /* The %g output depends on precision and the exponent of
res.range.min = 1; the argument. Since the value of the argument isn't known
the lower bound on the range of bytes (not counting flags
/* Compute the maximum just once. */ or width) is 1. */
const HOST_WIDE_INT g_max[] = { res.range.min = flagmin;
format_floating_max (double_type_node, 'g', prec), if (res.range.max == HOST_WIDE_INT_M1U)
format_floating_max (long_double_type_node, 'g', prec) {
}; /* Compute the upper bound for -TYPE_MAX which should be
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl]; the lesser of %e and %f. */
res.range.max = format_floating_max (type, 'g', prec);
/* "%g" is fully specified and the range of bytes is bounded }
unless width is unknown. */
res.bounded = INT_MIN != width;
break; break;
} }
...@@ -1453,6 +1463,7 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width, ...@@ -1453,6 +1463,7 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
if (width > 0) if (width > 0)
{ {
/* If width has been specified use it to adjust the range. */
if (res.range.min < (unsigned)width) if (res.range.min < (unsigned)width)
res.range.min = width; res.range.min = width;
if (res.range.max < (unsigned)width) if (res.range.max < (unsigned)width)
...@@ -1469,9 +1480,9 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width, ...@@ -1469,9 +1480,9 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
static fmtresult static fmtresult
format_floating (const conversion_spec &spec, tree arg) format_floating (const conversion_spec &spec, tree arg)
{ {
/* Set WIDTH to -1 when it's not specified, to INT_MIN when it is /* Set WIDTH to -1 when it's not specified, to HOST_WIDE_INT_MIN when
specified by the asterisk to an unknown value, and otherwise to it is specified by the asterisk to an unknown value, and otherwise
a non-negative value corresponding to the specified width. */ to a non-negative value corresponding to the specified width. */
HOST_WIDE_INT width = -1; HOST_WIDE_INT width = -1;
HOST_WIDE_INT prec = -1; HOST_WIDE_INT prec = -1;
...@@ -1498,13 +1509,13 @@ format_floating (const conversion_spec &spec, tree arg) ...@@ -1498,13 +1509,13 @@ format_floating (const conversion_spec &spec, tree arg)
else if (spec.star_precision) else if (spec.star_precision)
{ {
if (TREE_CODE (spec.star_precision) == INTEGER_CST) if (TREE_CODE (spec.star_precision) == INTEGER_CST)
prec = tree_to_shwi (spec.star_precision);
else
{ {
/* FIXME: Handle non-constant precision. */ prec = tree_to_shwi (spec.star_precision);
res.range.min = res.range.max = HOST_WIDE_INT_M1U; if (prec < 0)
return res; prec = -1;
} }
else
prec = INT_MIN;
} }
else if (res.constant && TOUPPER (spec.specifier) != 'A') else if (res.constant && TOUPPER (spec.specifier) != 'A')
{ {
...@@ -1515,11 +1526,6 @@ format_floating (const conversion_spec &spec, tree arg) ...@@ -1515,11 +1526,6 @@ format_floating (const conversion_spec &spec, tree arg)
if (res.constant) if (res.constant)
{ {
/* Set up an array to easily iterate over. */
unsigned HOST_WIDE_INT* const minmax[] = {
&res.range.min, &res.range.max
};
/* Get the real type format desription for the target. */ /* Get the real type format desription for the target. */
const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg); const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg);
const real_format *rfmt = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg))); const real_format *rfmt = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)));
...@@ -1541,6 +1547,12 @@ format_floating (const conversion_spec &spec, tree arg) ...@@ -1541,6 +1547,12 @@ format_floating (const conversion_spec &spec, tree arg)
*pfmt = '\0'; *pfmt = '\0';
{
/* Set up an array to easily iterate over below. */
unsigned HOST_WIDE_INT* const minmax[] = {
&res.range.min, &res.range.max
};
for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i) for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i)
{ {
/* Use the MPFR rounding specifier to round down in the first /* Use the MPFR rounding specifier to round down in the first
...@@ -1558,9 +1570,19 @@ format_floating (const conversion_spec &spec, tree arg) ...@@ -1558,9 +1570,19 @@ format_floating (const conversion_spec &spec, tree arg)
*minmax[i] = len; *minmax[i] = len;
} }
}
/* Make sure the minimum is less than the maximum (MPFR rounding
in the call to mpfr_snprintf can result in the reverse. */
if (res.range.max < res.range.min)
{
unsigned HOST_WIDE_INT tmp = res.range.min;
res.range.min = res.range.max;
res.range.max = tmp;
}
/* The range of output is known even if the result isn't bounded. */ /* The range of output is known even if the result isn't bounded. */
if (width == INT_MIN) if (width == HOST_WIDE_INT_MIN)
{ {
res.knownrange = false; res.knownrange = false;
res.range.max = HOST_WIDE_INT_MAX; res.range.max = HOST_WIDE_INT_MAX;
...@@ -2420,10 +2442,10 @@ pass_sprintf_length::compute_format_length (call_info &info, ...@@ -2420,10 +2442,10 @@ pass_sprintf_length::compute_format_length (call_info &info,
{ {
/* Similarly to the block above, this could be either a POSIX /* Similarly to the block above, this could be either a POSIX
positional argument or a width, depending on what follows. */ positional argument or a width, depending on what follows. */
if (gimple_call_num_args (info.callstmt) <= argno) if (argno < gimple_call_num_args (info.callstmt))
return false;
spec.star_width = gimple_call_arg (info.callstmt, argno++); spec.star_width = gimple_call_arg (info.callstmt, argno++);
else
spec.star_width = void_node;
++pf; ++pf;
} }
...@@ -2499,7 +2521,10 @@ pass_sprintf_length::compute_format_length (call_info &info, ...@@ -2499,7 +2521,10 @@ pass_sprintf_length::compute_format_length (call_info &info,
} }
else if ('*' == *pf) else if ('*' == *pf)
{ {
if (argno < gimple_call_num_args (info.callstmt))
spec.star_width = gimple_call_arg (info.callstmt, argno++); spec.star_width = gimple_call_arg (info.callstmt, argno++);
else
spec.star_width = void_node;
++pf; ++pf;
} }
else if ('\'' == *pf) else if ('\'' == *pf)
...@@ -2527,7 +2552,10 @@ pass_sprintf_length::compute_format_length (call_info &info, ...@@ -2527,7 +2552,10 @@ pass_sprintf_length::compute_format_length (call_info &info,
} }
else if ('*' == *pf) else if ('*' == *pf)
{ {
if (argno < gimple_call_num_args (info.callstmt))
spec.star_precision = gimple_call_arg (info.callstmt, argno++); spec.star_precision = gimple_call_arg (info.callstmt, argno++);
else
spec.star_precision = void_node;
++pf; ++pf;
} }
else else
...@@ -2795,11 +2823,11 @@ try_substitute_return_value (gimple_stmt_iterator *gsi, ...@@ -2795,11 +2823,11 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
fprintf (dump_file, fprintf (dump_file,
" %s-bounds return value in range [%lu, %lu]%s.\n", " %s-bounds return value in range [%lu, %lu]%s.\n",
inbounds, inbounds,
(unsigned long)res.number_chars_min, (unsigned long)res.number_chars_min - 1,
(unsigned long)res.number_chars_max, ign); (unsigned long)res.number_chars_max - 1, ign);
else else
fprintf (dump_file, " %s-bounds return value %lu%s.\n", fprintf (dump_file, " %s-bounds return value %lu%s.\n",
inbounds, (unsigned long)res.number_chars, ign); inbounds, (unsigned long)res.number_chars - 1, ign);
} }
} }
} }
......
2017-01-03 Martin Sebor <msebor@redhat.com>
PR tree-optimization/78696
* gcc.dg/tree-ssa/builtin-sprintf-5.c: Remove incorrect test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: Correct off-by-1 errors.
* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.
2017-01-03 David Malcolm <dmalcolm@redhat.com>
* gcc.dg/dg-test-1.c: Add tests of relative line specifications
with more than one digit.
* lib/gcc-dg.exp (process-message): Support more than one digit
in relative line specifications.
2017-01-03 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/78965
* gcc.dg/pr78965.c: New test.
PR middle-end/78901
* g++.dg/opt/pr78901.C: New test.
2017-01-03 Janne Blomqvist <jb@gcc.gnu.org>
PR fortran/78534
PR fortran/66310
* gfortran.dg/dependency_49.f90: Change scan-tree-dump-times
due to gfc_trans_string_copy change to avoid -Wstringop-overflow.
* gfortran.dg/repeat_4.f90: Use integers of kind C_SIZE_T.
* gfortran.dg/repeat_7.f90: New test for PR 66310.
* gfortran.dg/scan_2.f90: Handle potential cast in assignment.
* gfortran.dg/string_1.f90: Limit to ilp32 targets.
* gfortran.dg/string_1_lp64.f90: New test.
* gfortran.dg/string_3.f90: Limit to ilp32 targets.
* gfortran.dg/string_3_lp64.f90: New test.
* gfortran.dg/transfer_intrinsic_1.f90: Change
scan-tree-dump-times due to gfc_trans_string_copy change to
avoid -Wstringop-overflow.
2017-01-02 Uros Bizjak <ubizjak@gmail.com> 2017-01-02 Uros Bizjak <ubizjak@gmail.com>
PR target/78967 PR target/78967
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
FAIL (__LINE__)(value); \ FAIL (__LINE__)(value); \
} while (0) } while (0)
/* Verify that EXPECT == snprintf(0, 0, ...). */
#define EQL(expect, ...) \ #define EQL(expect, ...) \
do { \ do { \
int n = __builtin_snprintf (0, 0, __VA_ARGS__); \ int n = __builtin_snprintf (0, 0, __VA_ARGS__); \
...@@ -140,6 +141,7 @@ void test_arg_multiarg (int i, double d) ...@@ -140,6 +141,7 @@ void test_arg_multiarg (int i, double d)
EQL (16, "%*i %s", 12, i, "abc"); EQL (16, "%*i %s", 12, i, "abc");
} }
/* Verify that EXPECT == vsnprintf(0, 0, ...). */
#define EQLv(expect, fmt, va) \ #define EQLv(expect, fmt, va) \
do { \ do { \
int n = __builtin_vsnprintf (0, 0, fmt, va); \ int n = __builtin_vsnprintf (0, 0, fmt, va); \
...@@ -149,9 +151,7 @@ void test_arg_multiarg (int i, double d) ...@@ -149,9 +151,7 @@ void test_arg_multiarg (int i, double d)
void test_va_int (__builtin_va_list va) void test_va_int (__builtin_va_list va)
{ {
EQLv ( 2, "%02hhx", va); EQLv ( 2, "%02hhx", va);
EQLv ( 2, "%02.*hhx", va);
EQLv ( 4, "%04hx", va); EQLv ( 4, "%04hx", va);
EQLv ( 4, "%04.*hx", va);
} }
void test_va_multiarg (__builtin_va_list va) void test_va_multiarg (__builtin_va_list va)
......
...@@ -63,11 +63,14 @@ void test_floating_a_var (double x) ...@@ -63,11 +63,14 @@ void test_floating_a_var (double x)
T (0, "%.*a", INT_MIN, x); /* { dg-warning "writing between 6 and 24 bytes" } */ T (0, "%.*a", INT_MIN, x); /* { dg-warning "writing between 6 and 24 bytes" } */
T (0, "%.*a", INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483658 bytes" } */ /* Expected output is "0x0." followed by INT_MAX digits followed by
"p+" followed by 1 to four digits, with a byte count in the range
[3 + INT_MAX + 2 + 1, 3 + INT_MAX + 2 + 4]. */
T (0, "%.*a", INT_MAX, x); /* { dg-warning "writing between 2147483654 and 2147483658 bytes" } */
T (0, "%*.*a", INT_MIN, INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*.*a", INT_MIN, INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*.*a", INT_MAX, INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483658 bytes" } */ T (0, "%*.*a", INT_MAX, INT_MAX, x); /* { dg-warning "writing between 2147483654 and 2147483658 bytes" } */
} }
void test_floating_e_cst (void) void test_floating_e_cst (void)
...@@ -75,7 +78,7 @@ void test_floating_e_cst (void) ...@@ -75,7 +78,7 @@ void test_floating_e_cst (void)
T (0, "%*e", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*e", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*e", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ T (0, "%*e", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */
T (0, "%.*e", INT_MIN, 0.); /* { dg-warning "writing 5 bytes" } */ T (0, "%.*e", INT_MIN, 0.); /* { dg-warning "writing 12 bytes" } */
T (0, "%.*e", INT_MAX, 0.); /* { dg-warning "writing 2147483653 bytes" } */ T (0, "%.*e", INT_MAX, 0.); /* { dg-warning "writing 2147483653 bytes" } */
...@@ -103,7 +106,7 @@ void test_floating_f_cst (void) ...@@ -103,7 +106,7 @@ void test_floating_f_cst (void)
T (0, "%*f", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ T (0, "%*f", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*f", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ T (0, "%*f", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */
T (0, "%.*f", INT_MIN, 0.); /* { dg-warning "writing 1 byte" } */ T (0, "%.*f", INT_MIN, 0.); /* { dg-warning "writing 8 byte" } */
T (0, "%.*f", INT_MAX, 0.); /* { dg-warning "writing 2147483649 bytes" } */ T (0, "%.*f", INT_MAX, 0.); /* { dg-warning "writing 2147483649 bytes" } */
......
/* 78696 - -fprintf-return-value misoptimizes %.Ng where N is greater than 10
Test to verify the correctness of ranges of output computed for floating
point directives.
{ dg-do compile }
{ dg-options "-O2 -Wformat -Wformat-length -ftrack-macro-expansion=0" } */
typedef __builtin_va_list va_list;
char dst[1];
extern void sink (int, void*);
/* Macro to test either width or precision specified by the asterisk
(but not both). */
#define T1(fmt, a) sink (__builtin_sprintf (dst + 1, fmt, a, x), dst)
/* Macro to test both width and precision specified by the asterisk. */
#define T2(fmt, w, p) sink (__builtin_sprintf (dst + 1, fmt, w, p, x), dst)
/* Macro to test vsprintf with both width and precision specified by
the asterisk. */
#define T(fmt) sink (__builtin_vsprintf (dst + 1, fmt, va), dst)
/* Exercise %a. */
void test_a (int w, int p, double x)
{
T1 ("%.*a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%.*a", 1); /* { dg-warning "between 8 and 12 bytes" } */
T1 ("%.*a", 2); /* { dg-warning "between 9 and 13 bytes" } */
T1 ("%.*a", 99); /* { dg-warning "between 106 and 110 bytes" } */
T1 ("%.*a", 199); /* { dg-warning "between 206 and 210 bytes" } */
T1 ("%.*a", 1099); /* { dg-warning "between 1106 and 1110 bytes" } */
T1 ("%*.a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 1); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 3); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */
T1 ("%*.a", w); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%*.0a", w); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%*.1a", w); /* { dg-warning "writing 8 or more bytes" } */
T1 ("%*.2a", w); /* { dg-warning "writing 9 or more bytes" } */
T1 ("%.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%1.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%2.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%3.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
}
/* Exercise %e. */
void test_e (int w, int p, double x)
{
T1 ("%.*e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%.*e", 1); /* { dg-warning "between 7 and 9 bytes" } */
T1 ("%.*e", 2); /* { dg-warning "between 8 and 10 bytes" } */
T1 ("%.*e", 99); /* { dg-warning "between 105 and 107 bytes" } */
T1 ("%.*e", 199); /* { dg-warning "between 205 and 207 bytes" } */
T1 ("%.*e", 1099); /* { dg-warning "between 1105 and 1107 bytes" } */
T1 ("%*.e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 3); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */
T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */
T1 ("%*.e", w); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%*.0e", w); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%*.1e", w); /* { dg-warning "writing 7 or more bytes" } */
T1 ("%*.2e", w); /* { dg-warning "writing 8 or more bytes" } */
T1 ("%.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%1.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%2.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%3.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
}
/* Exercise %f. */
void test_f (int w, int p, double x)
{
T1 ("%.*f", 0); /* { dg-warning "between 1 and 310 bytes" } */
T1 ("%.*f", 1); /* { dg-warning "between 3 and 312 bytes" } */
T1 ("%.*f", 2); /* { dg-warning "between 4 and 313 bytes" } */
T1 ("%.*f", 99); /* { dg-warning "between 101 and 410 bytes" } */
T1 ("%.*f", 199); /* { dg-warning "between 201 and 510 bytes" } */
T1 ("%.*f", 1099); /* { dg-warning "between 1101 and 1410 bytes" } */
T2 ("%*.*f", 0, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 1, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 2, 0); /* { dg-warning "between 2 and 310 bytes" } */
T2 ("%*.*f", 3, 0); /* { dg-warning "between 3 and 310 bytes" } */
T2 ("%*.*f", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*f", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */
T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */
T1 ("%*.f", w); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%*.0f", w); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%*.1f", w); /* { dg-warning "writing 3 or more bytes" } */
T1 ("%*.2f", w); /* { dg-warning "writing 4 or more bytes" } */
T1 ("%.*f", p); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%1.*f", p); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%2.*f", p); /* { dg-warning "writing 2 or more bytes" } */
T1 ("%3.*f", p); /* { dg-warning "writing 3 or more bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
void test_g (double x)
{
T1 ("%.*g", 0); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 1); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 2); /* { dg-warning "between 1 and 9 bytes" } */
T1 ("%.*g", 99); /* { dg-warning "between 1 and 106 bytes" } */
T1 ("%.*g", 199); /* { dg-warning "between 1 and 206 bytes" } */
T1 ("%.*g", 1099); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*g", 0, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 1, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 2, 0); /* { dg-warning "between 2 and 7 bytes" } */
T2 ("%*.*g", 3, 0); /* { dg-warning "between 3 and 7 bytes" } */
T2 ("%*.*g", 7, 0); /* { dg-warning "writing 7 bytes" } */
T2 ("%*.*g", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*g", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*g", 312, 312); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 312, 313); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 333, 999); /* { dg-warning "writing 333 bytes" } */
}
/* Exercise %a. */
void test_a_va (va_list va)
{
T ("%.0a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%.1a"); /* { dg-warning "between 8 and 12 bytes" } */
T ("%.2a"); /* { dg-warning "between 9 and 13 bytes" } */
T ("%.99a"); /* { dg-warning "between 106 and 110 bytes" } */
T ("%.199a"); /* { dg-warning "between 206 and 210 bytes" } */
T ("%.1099a"); /* { dg-warning "between 1106 and 1110 bytes" } */
T ("%0.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%1.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%3.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */
T ("%*.a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%*.0a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%*.1a"); /* { dg-warning "writing 8 or more bytes" } */
T ("%*.2a"); /* { dg-warning "writing 9 or more bytes" } */
T ("%.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%1.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%2.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%6.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%9.*a"); /* { dg-warning "writing 9 or more bytes" } */
T ("%*.*a"); /* { dg-warning "writing 6 or more bytes" } */
}
/* Exercise %e. */
void test_e_va (va_list va)
{
T ("%e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("% e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%#e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%#+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%# e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.0e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.1e"); /* { dg-warning "between 7 and 9 bytes" } */
T ("%.2e"); /* { dg-warning "between 8 and 10 bytes" } */
T ("%.99e"); /* { dg-warning "between 105 and 107 bytes" } */
T ("%.199e"); /* { dg-warning "between 205 and 207 bytes" } */
T ("%.1099e"); /* { dg-warning "between 1105 and 1107 bytes" } */
T ("%0.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%3.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */
T ("%7.e"); /* { dg-warning "writing 7 bytes" } */
T ("%.*e"); /* { dg-warning "writing 5 or more bytes" } */
T ("%1.*e"); /* { dg-warning "writing 5 or more bytes" } */
T ("%6.*e"); /* { dg-warning "writing 6 or more bytes" } */
T ("%9.*e"); /* { dg-warning "writing 9 or more bytes" } */
T ("%*.*e"); /* { dg-warning "writing 5 or more bytes" } */
}
/* Exercise %f. */
void test_f_va (va_list va)
{
T ("%f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%# f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%.f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.1f"); /* { dg-warning "between 3 and 312 bytes" } */
T ("%.2f"); /* { dg-warning "between 4 and 313 bytes" } */
T ("%.99f"); /* { dg-warning "between 101 and 410 bytes" } */
T ("%.199f"); /* { dg-warning "between 201 and 510 bytes" } */
T ("%.1099f"); /* { dg-warning "between 1101 and 1410 bytes" } */
T ("%0.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%1.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%2.0f"); /* { dg-warning "between 2 and 310 bytes" } */
T ("%3.0f"); /* { dg-warning "between 3 and 310 bytes" } */
T ("%310.0f"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0f"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */
T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */
T ("%.*f"); /* { dg-warning "writing 1 or more bytes" } */
T ("%1.*f"); /* { dg-warning "writing 1 or more bytes" } */
T ("%3.*f"); /* { dg-warning "writing 3 or more bytes" } */
T ("%*.*f"); /* { dg-warning "writing 1 or more bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
void test_g_va (va_list va)
{
T ("%g"); /* { dg-warning "between 1 and 13 bytes" } */
T ("%+g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("% g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%#g"); /* { dg-warning "between 1 and 13 bytes" } */
T ("%#+g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%# g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.1g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.2g"); /* { dg-warning "between 1 and 9 bytes" } */
T ("%.99g"); /* { dg-warning "between 1 and 106 bytes" } */
T ("%.199g"); /* { dg-warning "between 1 and 206 bytes" } */
T ("%.1099g"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%0.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%1.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%2.0g"); /* { dg-warning "between 2 and 7 bytes" } */
T ("%3.0g"); /* { dg-warning "between 3 and 7 bytes" } */
T ("%7.0g"); /* { dg-warning "writing 7 bytes" } */
T ("%310.0g"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0g"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312g"); /* { dg-warning "writing 312 bytes" } */
T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */
T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */
T ("%.*g"); /* { dg-warning "writing 1 or more bytes" } */
T ("%1.*g"); /* { dg-warning "writing 1 or more bytes" } */
T ("%4.*g"); /* { dg-warning "writing 4 or more bytes" } */
T ("%*.*g"); /* { dg-warning "writing 1 or more bytes" } */
}
...@@ -56,9 +56,12 @@ checkv (const char *func, int line, int res, int min, int max, ...@@ -56,9 +56,12 @@ checkv (const char *func, int line, int res, int min, int max,
fail = 1; fail = 1;
} }
else else if (min == max)
__builtin_printf ("PASS: %s:%i: \"%s\" result %i: \"%s\"\n", __builtin_printf ("PASS: %s:%i: \"%s\" result %i: \"%s\"\n",
func, line, fmt, n, dst); func, line, fmt, n, dst);
else
__builtin_printf ("PASS: %s:%i: \"%s\" result %i in [%i, %i]: \"%s\"\n",
func, line, fmt, n, min, max, dst);
} }
if (fail) if (fail)
...@@ -75,7 +78,7 @@ check (const char *func, int line, int res, int min, int max, ...@@ -75,7 +78,7 @@ check (const char *func, int line, int res, int min, int max,
__builtin_va_end (va); __builtin_va_end (va);
} }
char buffer[256]; char buffer[4100];
char* volatile dst = buffer; char* volatile dst = buffer;
char* ptr = buffer; char* ptr = buffer;
...@@ -415,6 +418,8 @@ test_a_double (double d) ...@@ -415,6 +418,8 @@ test_a_double (double d)
EQL ( 9, 10, "%.2a", 4.0); /* 0x8.00p-1 */ EQL ( 9, 10, "%.2a", 4.0); /* 0x8.00p-1 */
EQL (10, 11, "%.3a", 5.0); /* 0xa.000p-1 */ EQL (10, 11, "%.3a", 5.0); /* 0xa.000p-1 */
EQL (11, 12, "%.*a", 4, 6.0); /* 0xc.0000p-1 */
EQL (12, 13, "%.*a", 5, 7.0); /* 0xe.00000p-1 */
/* d is in [ 0, -DBL_MAX ] */ /* d is in [ 0, -DBL_MAX ] */
RNG ( 6, 10, 11, "%.0a", d); /* 0x0p+0 ... -0x2p+1023 */ RNG ( 6, 10, 11, "%.0a", d); /* 0x0p+0 ... -0x2p+1023 */
RNG ( 6, 12, 13, "%.1a", d); /* 0x0p+0 ... -0x2.0p+1023 */ RNG ( 6, 12, 13, "%.1a", d); /* 0x0p+0 ... -0x2.0p+1023 */
...@@ -432,7 +437,7 @@ test_a_long_double (void) ...@@ -432,7 +437,7 @@ test_a_long_double (void)
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_e_double (void) test_e_double (double d)
{ {
EQL (12, 13, "%e", 1.0e0); EQL (12, 13, "%e", 1.0e0);
EQL (13, 14, "%e", -1.0e0); EQL (13, 14, "%e", -1.0e0);
...@@ -454,10 +459,34 @@ test_e_double (void) ...@@ -454,10 +459,34 @@ test_e_double (void)
EQL (12, 13, "%e", 1.0e-1); EQL (12, 13, "%e", 1.0e-1);
EQL (12, 13, "%e", 1.0e-12); EQL (12, 13, "%e", 1.0e-12);
EQL (13, 14, "%e", 1.0e-123); EQL (13, 14, "%e", 1.0e-123);
RNG (12, 14, 15, "%e", d);
RNG ( 5, 7, 8, "%.e", d);
RNG ( 5, 7, 8, "%.0e", d);
RNG ( 7, 9, 10, "%.1e", d);
RNG ( 8, 10, 11, "%.2e", d);
RNG ( 9, 11, 12, "%.3e", d);
RNG (10, 12, 13, "%.4e", d);
RNG (11, 13, 14, "%.5e", d);
RNG (12, 14, 15, "%.6e", d);
RNG (13, 15, 16, "%.7e", d);
RNG (4006, 4008, 4009, "%.4000e", d);
RNG ( 5, 7, 8, "%.*e", 0, d);
RNG ( 7, 9, 10, "%.*e", 1, d);
RNG ( 8, 10, 11, "%.*e", 2, d);
RNG ( 9, 11, 12, "%.*e", 3, d);
RNG (10, 12, 13, "%.*e", 4, d);
RNG (11, 13, 14, "%.*e", 5, d);
RNG (12, 14, 15, "%.*e", 6, d);
RNG (13, 15, 16, "%.*e", 7, d);
RNG (4006, 4008, 4009, "%.*e", 4000, d);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_e_long_double (void) test_e_long_double (long double d)
{ {
EQL (12, 13, "%Le", 1.0e0L); EQL (12, 13, "%Le", 1.0e0L);
EQL (13, 14, "%Le", -1.0e0L); EQL (13, 14, "%Le", -1.0e0L);
...@@ -490,10 +519,32 @@ test_e_long_double (void) ...@@ -490,10 +519,32 @@ test_e_long_double (void)
EQL ( 8, 9, "%.1Le", 1.0e-111L); EQL ( 8, 9, "%.1Le", 1.0e-111L);
EQL (19, 20, "%.12Le", 1.0e-112L); EQL (19, 20, "%.12Le", 1.0e-112L);
EQL (20, 21, "%.13Le", 1.0e-113L); EQL (20, 21, "%.13Le", 1.0e-113L);
/* The following correspond to the double results plus 1 for the upper
bound accounting for the four-digit exponent. */
RNG (12, 15, 16, "%Le", d); /* 0.000000e+00 ... -1.189732e+4932 */
RNG ( 5, 8, 9, "%.Le", d);
RNG ( 5, 9, 10, "%.0Le", d);
RNG ( 7, 10, 11, "%.1Le", d); /* 0.0e+00 ... -1.2e+4932 */
RNG ( 8, 11, 12, "%.2Le", d); /* 0.00e+00 ... -1.19e+4932 */
RNG ( 9, 12, 13, "%.3Le", d);
RNG (10, 13, 14, "%.4Le", d);
RNG (11, 14, 15, "%.5Le", d);
RNG (12, 15, 16, "%.6Le", d); /* same as plain "%Le" */
RNG (13, 16, 17, "%.7Le", d); /* 0.0000000e+00 ... -1.1897315e+4932 */
RNG ( 5, 9, 10, "%.*Le", 0, d);
RNG ( 7, 10, 11, "%.*Le", 1, d);
RNG ( 8, 11, 12, "%.*Le", 2, d);
RNG ( 9, 12, 13, "%.*Le", 3, d);
RNG (10, 13, 14, "%.*Le", 4, d);
RNG (11, 14, 15, "%.*Le", 5, d);
RNG (12, 15, 16, "%.*Le", 6, d);
RNG (13, 16, 17, "%.*Le", 7, d);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_f_double (void) test_f_double (double d)
{ {
EQL ( 8, 9, "%f", 0.0e0); EQL ( 8, 9, "%f", 0.0e0);
EQL ( 8, 9, "%f", 0.1e0); EQL ( 8, 9, "%f", 0.1e0);
...@@ -511,6 +562,8 @@ test_f_double (void) ...@@ -511,6 +562,8 @@ test_f_double (void)
EQL ( 8, 9, "%f", 1.0e-1); EQL ( 8, 9, "%f", 1.0e-1);
EQL ( 8, 9, "%f", 1.0e-12); EQL ( 8, 9, "%f", 1.0e-12);
EQL ( 8, 9, "%f", 1.0e-123); EQL ( 8, 9, "%f", 1.0e-123);
RNG ( 8, 317, 318, "%f", d);
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
...@@ -535,6 +588,87 @@ test_f_long_double (void) ...@@ -535,6 +588,87 @@ test_f_long_double (void)
} }
static void __attribute__ ((noinline, noclone)) static void __attribute__ ((noinline, noclone))
test_g_double (double d)
{
/* Numbers exactly representable in binary floating point. */
EQL ( 1, 2, "%g", 0.0);
EQL ( 3, 4, "%g", 1.0 / 2);
EQL ( 4, 5, "%g", 1.0 / 4);
EQL ( 5, 6, "%g", 1.0 / 8);
EQL ( 6, 7, "%g", 1.0 / 16);
EQL ( 7, 8, "%g", 1.0 / 32);
EQL ( 8, 9, "%g", 1.0 / 64);
EQL ( 9, 10, "%g", 1.0 / 128);
EQL ( 10, 11, "%g", 1.0 / 256);
EQL ( 10, 11, "%g", 1.0 / 512);
/* Numbers that are not exactly representable. */
RNG ( 3, 8, 9, "%g", 0.1);
RNG ( 4, 8, 9, "%g", 0.12);
RNG ( 5, 8, 9, "%g", 0.123);
RNG ( 6, 8, 9, "%g", 0.1234);
RNG ( 7, 8, 9, "%g", 0.12345);
RNG ( 8, 8, 9, "%g", 0.123456);
RNG ( 4, 7, 8, "%g", 0.123e+1);
EQL ( 8, 9, "%g", 0.123e+12);
RNG ( 9, 12, 13, "%g", 0.123e+134);
RNG ( 1, 13, 14, "%g", d);
RNG ( 1, 7, 8, "%.g", d);
RNG ( 1, 7, 8, "%.0g", d);
RNG ( 1, 7, 8, "%.1g", d);
RNG ( 1, 9, 10, "%.2g", d);
RNG ( 1, 10, 11, "%.3g", d);
RNG ( 1, 11, 12, "%.4g", d);
RNG ( 1, 12, 13, "%.5g", d);
RNG ( 1, 13, 14, "%.6g", d);
RNG ( 1, 14, 15, "%.7g", d);
RNG ( 1, 15, 16, "%.8g", d);
RNG ( 1,310,311, "%.9999g", d);
RNG ( 1, 7, 8, "%.*g", 0, d);
RNG ( 1, 7, 8, "%.*g", 1, d);
RNG ( 1, 9, 10, "%.*g", 2, d);
RNG ( 1, 10, 11, "%.*g", 3, d);
RNG ( 1, 11, 12, "%.*g", 4, d);
RNG ( 1, 12, 13, "%.*g", 5, d);
RNG ( 1, 13, 14, "%.*g", 6, d);
RNG ( 1, 14, 15, "%.*g", 7, d);
RNG ( 1, 15, 16, "%.*g", 8, d);
RNG ( 1,310,311, "%.*g", 9999, d);
}
static void __attribute__ ((noinline, noclone))
test_g_long_double (void)
{
/* Numbers exactly representable in binary floating point. */
EQL ( 1, 2, "%Lg", 0.0L);
EQL ( 3, 4, "%Lg", 1.0L / 2);
EQL ( 4, 5, "%Lg", 1.0L / 4);
EQL ( 5, 6, "%Lg", 1.0L / 8);
EQL ( 6, 7, "%Lg", 1.0L / 16);
EQL ( 7, 8, "%Lg", 1.0L / 32);
EQL ( 8, 9, "%Lg", 1.0L / 64);
EQL ( 9, 10, "%Lg", 1.0L / 128);
EQL ( 10, 11, "%Lg", 1.0L / 256);
EQL ( 10, 11, "%Lg", 1.0L / 512);
/* Numbers that are not exactly representable. */
RNG ( 3, 8, 9, "%Lg", 0.1L);
RNG ( 4, 8, 9, "%Lg", 0.12L);
RNG ( 5, 8, 9, "%Lg", 0.123L);
RNG ( 6, 8, 9, "%Lg", 0.1234L);
RNG ( 7, 8, 9, "%Lg", 0.12345L);
RNG ( 8, 8, 9, "%Lg", 0.123456L);
RNG ( 4, 7, 8, "%Lg", 0.123e+1L);
EQL ( 8, 9, "%Lg", 0.123e+12L);
RNG ( 9, 12, 13, "%Lg", 0.123e+134L);
}
static void __attribute__ ((noinline, noclone))
test_s (int i) test_s (int i)
{ {
EQL ( 0, 1, "%s", ""); EQL ( 0, 1, "%s", "");
...@@ -577,12 +711,14 @@ int main (void) ...@@ -577,12 +711,14 @@ int main (void)
test_x ('?', 0xdead, 0xdeadbeef); test_x ('?', 0xdead, 0xdeadbeef);
test_a_double (0.0); test_a_double (0.0);
test_e_double (); test_e_double (0.0);
test_f_double (); test_f_double (0.0);
test_g_double (0.0);
test_a_long_double (); test_a_long_double ();
test_e_long_double (); test_e_long_double (0.0);
test_f_long_double (); test_f_long_double ();
test_g_long_double ();
test_s (0); test_s (0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment