This is the mail archive of the libc-hacker@sources.redhat.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


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

[PATCH] Fix fcvt


Hi!

fcvt(332.5, 16, &x, &y) returns wrongly "322.500000000000000".
Early glibc 2.1 (such as in RHL 6.1) returned "3225000000000000000",
Solaris 7 does the same.
The issue is that unlike ecvt which may really take at most DBL_DIG digits
after decimal point plus one digit before plus final '\0' and internally '.'
as well, fcvt may take DBL_MAX_10_EXP plus DBL_DIG + '\0' and internally
'.' because ndigit specifies how many digits are printed after decimal
point.
SuS2 is not very concrete here, it sais the length of the resulting string
is restricted to an unspecified limit as determined by the precision of a
double.
As this is huge, I use the static buffer for short requests and as soon as
first fails due to short buffer, I try to malloc it.

2000-12-13  Jakub Jelinek  <jakub@redhat.com>

	* misc/efgcvt.c (FCVT_MAXDIG): Define.
	(FCVT_BUFPTR): New variable.
	(fcvt): If fcvt_r returns -1 on the static short buffer,
	try to malloc a sufficiently large one and retry.
	(free_mem): New function.
	* misc/qefgcvt.c (FCVT_MAXDIG): Define.
	* misc/tst-efgcvt.c (fcvt_tests): Add new test.

--- libc/misc/efgcvt.c.jj	Fri Jul 30 00:34:45 1999
+++ libc/misc/efgcvt.c	Wed Dec 13 12:29:29 2000
@@ -31,6 +31,7 @@
 /* Actually we have to write (DBL_DIG + log10 (DBL_MAX_10_EXP)) but we
    don't have log10 available in the preprocessor.  */
 # define MAXDIG (NDIGIT_MAX + 3)
+# define FCVT_MAXDIG (DBL_MAX_10_EXP + MAXDIG)
 # if DBL_MANT_DIG == 53
 #  define NDIGIT_MAX 17
 # elif DBL_MANT_DIG == 24
@@ -50,22 +51,34 @@
 
 
 #define FCVT_BUFFER APPEND (FUNC_PREFIX, fcvt_buffer)
+#define FCVT_BUFPTR APPEND (FUNC_PREFIX, fcvt_bufptr)
 #define ECVT_BUFFER APPEND (FUNC_PREFIX, ecvt_buffer)
 
 
 static char FCVT_BUFFER[MAXDIG];
 static char ECVT_BUFFER[MAXDIG];
-
+static char *FCVT_BUFPTR;
 
 char *
 APPEND (FUNC_PREFIX, fcvt) (value, ndigit, decpt, sign)
      FLOAT_TYPE value;
      int ndigit, *decpt, *sign;
 {
+  if (FCVT_BUFPTR == NULL)
+    {
+      if (APPEND (FUNC_PREFIX, fcvt_r) (value, ndigit, decpt, sign,
+					FCVT_BUFFER, MAXDIG) != -1)
+	return FCVT_BUFFER;
+
+      FCVT_BUFPTR = (char *) malloc (FCVT_MAXDIG);
+      if (FCVT_BUFPTR == NULL)
+	return FCVT_BUFFER;
+    }
+
   (void) APPEND (FUNC_PREFIX, fcvt_r) (value, ndigit, decpt, sign,
-				       FCVT_BUFFER, MAXDIG);
+				       FCVT_BUFPTR, FCVT_MAXDIG);
 
-  return FCVT_BUFFER;
+  return FCVT_BUFPTR;
 }
 
 
@@ -89,3 +102,13 @@ APPEND (FUNC_PREFIX, gcvt) (value, ndigi
   sprintf (buf, "%.*" FLOAT_FMT_FLAG "g", MIN (ndigit, NDIGIT_MAX), value);
   return buf;
 }
+
+/* Free all resources if necessary.  */
+static void __attribute__ ((unused))
+free_mem (void)
+{
+  if (FCVT_BUFPTR != NULL)
+    free (FCVT_BUFPTR);
+}                  
+
+text_set_element (__libc_subfreeres, free_mem);
--- libc/misc/qefgcvt.c.jj	Fri Jul 30 00:34:45 1999
+++ libc/misc/qefgcvt.c	Wed Dec 13 12:17:50 2000
@@ -26,6 +26,7 @@
    we don't have log10 available in the preprocessor.  Since we cannot
    assume anything on the used `long double' format be generous.  */
 #define MAXDIG (NDIGIT_MAX + 12)
+#define FCVT_MAXDIG (LDBL_MAX_10_EXP + MAXDIG)
 #if LDBL_MANT_DIG == 64
 # define NDIGIT_MAX 21
 #elif LDBL_MANT_DIG == 53
--- libc/misc/tst-efgcvt.c.jj	Thu Jul 27 15:59:35 2000
+++ libc/misc/tst-efgcvt.c	Wed Dec 13 12:42:14 2000
@@ -81,6 +81,7 @@ static testcase fcvt_tests[] =
   { 100.01, -4, 3, "100" },
   { 123.01, -4, 3, "100" },
   { 126.71, -4, 3, "100" },
+  { 322.5, 16, 3, "3225000000000000000" },
   /* -1.0 is end marker.  */
   { -1.0, 0, 0, "" }
 };

	Jakub

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