maint: update copyright
[gnulib.git] / lib / ftoastr.c
index ddc5251..7780d00 100644 (file)
@@ -1,6 +1,6 @@
 /* floating point to accurate string
 
-   Copyright (C) 2010 Free Software Foundation, Inc.
+   Copyright (C) 2010-2014 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 /* Written by Paul Eggert.  */
 
+/* This code can misbehave on some buggy or older platforms, when
+   operating on arguments on floating types other than 'double', or
+   when given unusual combinations of options.  Gnulib's
+   snprintf-posix module works around many of these problems.
+
+   This code relies on sprintf, strtod, etc. operating accurately;
+   otherwise, the resulting strings could be inaccurate or too long.  */
+
+#include <config.h>
+
 #include "ftoastr.h"
 
-#include "intprops.h"
 #include <float.h>
 #include <stdio.h>
 #include <stdlib.h>
 # define FLOAT_MIN LDBL_MIN
 # define FLOAT_PREC_BOUND _GL_LDBL_PREC_BOUND
 # define FTOASTR ldtoastr
-# define STRTOF strtold
+# if HAVE_C99_STRTOLD
+#  define STRTOF strtold
+# endif
 #elif LENGTH == 2
 # define FLOAT double
 # define FLOAT_DIG DBL_DIG
 # define FLOAT_MIN DBL_MIN
 # define FLOAT_PREC_BOUND _GL_DBL_PREC_BOUND
 # define FTOASTR dtoastr
-# define STRTOF strtod
 #else
 # define LENGTH 1
 # define FLOAT float
 # define FLOAT_MIN FLT_MIN
 # define FLOAT_PREC_BOUND _GL_FLT_PREC_BOUND
 # define FTOASTR ftoastr
-# define STRTOF strtof
+# if HAVE_STRTOF
+#  define STRTOF strtof
+# endif
 #endif
 
 /* On pre-C99 hosts, approximate strtof and strtold with strtod.  This
    may generate one or two extra digits, but that's better than not
-   working at all.  Assume that strtof works if strtold does.  */
-#if LENGTH != 2 && ! HAVE_C99_STRTOLD
-# undef STRTOF
+   working at all.  */
+#ifndef STRTOF
 # define STRTOF strtod
 #endif
 
+/* On hosts where it's not known that snprintf works, use sprintf to
+   implement the subset needed here.  Typically BUFSIZE is big enough
+   and there's little or no performance hit.  */
+#if ! GNULIB_SNPRINTF
+# undef snprintf
+# define snprintf ftoastr_snprintf
+static int
+ftoastr_snprintf (char *buf, size_t bufsize, char const *format,
+                  int width, int prec, FLOAT x)
+{
+  char width_0_buffer[LENGTH == 1 ? FLT_BUFSIZE_BOUND
+                      : LENGTH == 2 ? DBL_BUFSIZE_BOUND
+                      : LDBL_BUFSIZE_BOUND];
+  int n = width;
+  if (bufsize < sizeof width_0_buffer)
+    {
+      n = sprintf (width_0_buffer, format, 0, prec, x);
+      if (n < 0)
+        return n;
+      if (n < width)
+        n = width;
+    }
+  if (n < bufsize)
+    n = sprintf (buf, format, width, prec, x);
+  return n;
+}
+#endif
+
 int
 FTOASTR (char *buf, size_t bufsize, int flags, int width, FLOAT x)
 {
@@ -64,7 +103,8 @@ FTOASTR (char *buf, size_t bufsize, int flags, int width, FLOAT x)
 
      Florian Loitsch, Printing floating-point numbers quickly and accurately
      with integers.  ACM SIGPLAN notices 46, 6 (June 2010), 233-243
-     <http://dx.doi.org/10.1145/1809028.1806623>.  */
+     <http://dx.doi.org/10.1145/1809028.1806623>; also see the
+     2010-03-21 draft <http://florian.loitsch.com/tmp/article.pdf>.  */
 
   char format[sizeof "%-+ 0*.*Lg"];
   FLOAT abs_x = x < 0 ? -x : x;