Re: [PATCH] correct handling of negative-positive precision ranges with floating directives (PR 79800)

2017-03-14 Thread Jeff Law

On 03/13/2017 06:33 PM, Martin Sebor wrote:

The output of a floating point directive whose precision is
specified by an asterisk with an argument that's in a range
that includes both negative and positive values less than
six may include between zero and six fractional digits plus
a decimal point.  For example,

  printf ("%.*e", p, x);

results in the 14 bytes

  -1.797693e+308

when p == -1 and x == -DBL_MIN because a negative precision
is ignored and the directive assumes the default 6, and in
just the one byte

  0

when p == 0 and x == 0.0.

Current trunk doesn't handle this case correctly when the
floating argument isn't known and uses the upper bound of
the specified precision as the maximum number of fractional
digits.  As a result, it sets the range on the return value
in this case as [5, 7] (plus 5 for the longest multibyte
decimal decimal point) when the correct range is [5, 14] as
explained above (plus 5 again).

The attached patch corrects the handling of such precision
ranges to avoid this unlikely bug.

Martin

gcc-79800.diff


PR tree-optimization/79800 - wrong snprintf result range with precision in a 
narrow negative-positive range

gcc/ChangeLog:

PR tree-optimization/79800
* gimple-ssa-sprintf.c (format_floating: Add argument.  Handle
precision in negative-positive range.
(format_floating): Call non-const overload with adjusted precision.

gcc/testsuite/ChangeLog:

PR tree-optimization/79800
* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Add test cases.
* gcc.dg/tree-ssa/pr79800.c: New test.

Thanks.  Installed.

jeff



[PATCH] correct handling of negative-positive precision ranges with floating directives (PR 79800)

2017-03-13 Thread Martin Sebor

The output of a floating point directive whose precision is
specified by an asterisk with an argument that's in a range
that includes both negative and positive values less than
six may include between zero and six fractional digits plus
a decimal point.  For example,

  printf ("%.*e", p, x);

results in the 14 bytes

  -1.797693e+308

when p == -1 and x == -DBL_MIN because a negative precision
is ignored and the directive assumes the default 6, and in
just the one byte

  0

when p == 0 and x == 0.0.

Current trunk doesn't handle this case correctly when the
floating argument isn't known and uses the upper bound of
the specified precision as the maximum number of fractional
digits.  As a result, it sets the range on the return value
in this case as [5, 7] (plus 5 for the longest multibyte
decimal decimal point) when the correct range is [5, 14] as
explained above (plus 5 again).

The attached patch corrects the handling of such precision
ranges to avoid this unlikely bug.

Martin
PR tree-optimization/79800 - wrong snprintf result range with precision in a narrow negative-positive range

gcc/ChangeLog:

	PR tree-optimization/79800
	* gimple-ssa-sprintf.c (format_floating: Add argument.  Handle
	precision in negative-positive range.
	(format_floating): Call non-const overload with adjusted precision.

gcc/testsuite/ChangeLog:

	PR tree-optimization/79800
	* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Add test cases.
	* gcc.dg/tree-ssa/pr79800.c: New test.

diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 0448b21..2474391 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -1499,11 +1499,13 @@ format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
 }
 
 /* Return a range representing the minimum and maximum number of bytes
-   that the directive DIR will output for any argument.  This function
-   is used when the directive argument or its value isn't known.  */
+   that the directive DIR will output for any argument.  PREC gives
+   the adjusted precision range to account for negative precisions
+   meaning the default 6.  This function is used when the directive
+   argument or its value isn't known.  */
 
 static fmtresult
-format_floating (const directive )
+format_floating (const directive , const HOST_WIDE_INT prec[2])
 {
   tree type;
 
@@ -1532,8 +1534,8 @@ format_floating (const directive )
   /* The minimum output as determined by flags.  It's always at least 1.
  When plus or space are set the output is preceded by either a sign
  or a space.  */
-  int flagmin = (1 /* for the first digit */
-		 + (dir.get_flag ('+') | dir.get_flag (' ')));
+  unsigned flagmin = (1 /* for the first digit */
+		  + (dir.get_flag ('+') | dir.get_flag (' ')));
 
   /* When the pound flag is set the decimal point is included in output
  regardless of precision.  Whether or not a decimal point is included
@@ -1557,14 +1559,13 @@ format_floating (const directive )
 			 + minprec
 			 + 3 /* p+0 */);
 
-	res.range.max = format_floating_max (type, 'a', dir.prec[1]);
+	res.range.max = format_floating_max (type, 'a', prec[1]);
 	res.range.likely = res.range.min;
 
 	/* The unlikely maximum accounts for the longest multibyte
 	   decimal point character.  */
 	res.range.unlikely = res.range.max;
-	if (dir.prec[0] != dir.prec[1]
-	|| dir.prec[0] == -1 || dir.prec[0] > 0)
+	if (dir.prec[1] > 0)
 	  res.range.unlikely += target_mb_len_max () - 1;
 
 	break;
@@ -1573,23 +1574,18 @@ format_floating (const directive )
 case 'E':
 case 'e':
   {
+	/* Minimum output attributable to precision and, when it's
+	   non-zero, decimal point.  */
+	HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
+
 	/* The minimum output is "[-+]1.234567e+00" regardless
 	   of the value of the actual argument.  */
-	HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
-	if ((dir.prec[0] < 0 && dir.prec[1] > -1) || dir.prec[0] == 0)
-	  minprec = 0;
-	else if (dir.prec[0] > 0)
-	  minprec = dir.prec[0] + !radix /* decimal point */;
-
 	res.range.min = (flagmin
 			 + radix
 			 + minprec
 			 + 2 /* e+ */ + 2);
-	/* MPFR uses a precision of 16 by default for some reason.
-	   Set it to the C default of 6.  */
-	int maxprec = dir.prec[1] < 0 ? 6 : dir.prec[1];
-	res.range.max = format_floating_max (type, 'e', maxprec);
 
+	res.range.max = format_floating_max (type, 'e', prec[1]);
 	res.range.likely = res.range.min;
 
 	/* The unlikely maximum accounts for the longest multibyte
@@ -1605,21 +1601,19 @@ format_floating (const directive )
 case 'F':
 case 'f':
   {
+	/* Minimum output attributable to precision and, when it's non-zero,
+	   decimal point.  */
+	HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
+
 	/* The lower bound when precision isn't specified is 8 bytes
 	   ("1.23456" since precision is taken to be 6).  When precision
 	   is zero, the lower bound is 1 byte (e.g., "1").  Otherwise,
 	   when precision is greater than zero, then