This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Fix strtod rounding of hex values (bug 14049)


The strtod implementation, when handling a hexadecimal value with more
digits than needed to compute the mantissa, passes a value "dig_no >
0" as the last argument to round_and_return.  The meaning of this
argument is whether any subsequent bits (after the mantissa and
subsequent MP limb that includes at least the half-ulp bit) are
nonzero.

dig_no here counts the number of remaining digits *including* the one
being processed, so is always positive here and half-way values always
get rounded away from zero.  Furthermore, while trailing zeros after
'.' have been excluded from consideration earlier, those before '.'
have not.

I propose this patch to fix this bug by checking the rest of the
string for nonzero digits.  Because of the earlier handling of
trailing zeros, there is no need to check for '.' here; if it occurs
before dig_no becomes 0, there must be a nonzero digit after the '.'
(and so '.' can be treated like a nonzero digit).  Tested x86_64.

2012-05-02  Joseph Myers  <joseph@codesourcery.com>

	[BZ #14049]
	* stdlib/strtod_l.c (____STRTOF_INTERNAL): Check for trailing
	nonzero digits before rounding a hex value.
	* stdlib/tst-strtod.c (tests): Add another test.

diff --git a/stdlib/strtod_l.c b/stdlib/strtod_l.c
index 93ead75..2166a08 100644
--- a/stdlib/strtod_l.c
+++ b/stdlib/strtod_l.c
@@ -1,6 +1,5 @@
 /* Convert string representing a number to float value, using given locale.
-   Copyright (C) 1997,1998,2002,2004,2005,2006,2007,2008,2009,2010,2011
-   Free Software Foundation, Inc.
+   Copyright (C) 1997-2012 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
 
@@ -994,8 +993,20 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
 	      retval[idx--] |= val >> (4 - pos - 1);
 	      val <<= BITS_PER_MP_LIMB - (4 - pos - 1);
 	      if (idx < 0)
-		return round_and_return (retval, exponent, negative, val,
-					 BITS_PER_MP_LIMB - 1, dig_no > 0);
+		{
+		  int rest_nonzero = 0;
+		  while (--dig_no > 0)
+		    {
+		      if (*startp != L_('0'))
+			{
+			  rest_nonzero = 1;
+			  break;
+			}
+		      startp++;
+		    }
+		  return round_and_return (retval, exponent, negative, val,
+					   BITS_PER_MP_LIMB - 1, rest_nonzero);
+		}
 
 	      retval[idx] = val;
 	      pos = BITS_PER_MP_LIMB - 1 - (4 - pos - 1);
diff --git a/stdlib/tst-strtod.c b/stdlib/tst-strtod.c
index 25bee78..738e73e 100644
--- a/stdlib/tst-strtod.c
+++ b/stdlib/tst-strtod.c
@@ -69,6 +69,11 @@ static const struct ltest tests[] =
     { "+InFiNiTy", HUGE_VAL, '\0', 0 },
     { "0x80000Ap-23", 0x80000Ap-23, '\0', 0 },
     { "1e-324", 0, '\0', ERANGE },
+    { "0x100000000000008p0", 0x1p56, '\0', 0 },
+    { "0x100000000000008.p0", 0x1p56, '\0', 0 },
+    { "0x100000000000008.00p0", 0x1p56, '\0', 0 },
+    { "0x10000000000000800p0", 0x1p64, '\0', 0 },
+    { "0x10000000000000801p0", 0x1.0000000000001p64, '\0', 0 },
     { NULL, 0, '\0', 0 }
   };
 

-- 
Joseph S. Myers
joseph@codesourcery.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]