112305
|
1 /* floating point to accurate string
|
|
2
|
|
3 Copyright (C) 2010-2011 Free Software Foundation, Inc.
|
|
4
|
|
5 This program is free software: you can redistribute it and/or modify
|
|
6 it under the terms of the GNU General Public License as published by
|
|
7 the Free Software Foundation; either version 3 of the License, or
|
|
8 (at your option) any later version.
|
|
9
|
|
10 This program is distributed in the hope that it will be useful,
|
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13 GNU General Public License for more details.
|
|
14
|
|
15 You should have received a copy of the GNU General Public License
|
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
17
|
|
18 /* Written by Paul Eggert. */
|
|
19
|
|
20 /* This code can misbehave on some buggy or older platforms, when
|
|
21 operating on arguments on floating types other than 'double', or
|
|
22 when given unusual combinations of options. Gnulib's
|
|
23 snprintf-posix module works around many of these problems.
|
|
24
|
|
25 This code relies on sprintf, strtod, etc. operating accurately;
|
|
26 otherwise, the resulting strings could be inaccurate or too long. */
|
|
27
|
|
28 #include <config.h>
|
|
29
|
|
30 #include "ftoastr.h"
|
|
31
|
|
32 #include "intprops.h"
|
|
33 #include <float.h>
|
|
34 #include <stdio.h>
|
|
35 #include <stdlib.h>
|
|
36
|
|
37 #if LENGTH == 3
|
|
38 # define FLOAT long double
|
|
39 # define FLOAT_DIG LDBL_DIG
|
|
40 # define FLOAT_MIN LDBL_MIN
|
|
41 # define FLOAT_PREC_BOUND _GL_LDBL_PREC_BOUND
|
|
42 # define FTOASTR ldtoastr
|
|
43 # define STRTOF strtold
|
|
44 #elif LENGTH == 2
|
|
45 # define FLOAT double
|
|
46 # define FLOAT_DIG DBL_DIG
|
|
47 # define FLOAT_MIN DBL_MIN
|
|
48 # define FLOAT_PREC_BOUND _GL_DBL_PREC_BOUND
|
|
49 # define FTOASTR dtoastr
|
|
50 # define STRTOF strtod
|
|
51 #else
|
|
52 # define LENGTH 1
|
|
53 # define FLOAT float
|
|
54 # define FLOAT_DIG FLT_DIG
|
|
55 # define FLOAT_MIN FLT_MIN
|
|
56 # define FLOAT_PREC_BOUND _GL_FLT_PREC_BOUND
|
|
57 # define FTOASTR ftoastr
|
|
58 # define STRTOF strtof
|
|
59 #endif
|
|
60
|
|
61 /* On pre-C99 hosts, approximate strtof and strtold with strtod. This
|
|
62 may generate one or two extra digits, but that's better than not
|
|
63 working at all. Assume that strtof works if strtold does. */
|
|
64 #if LENGTH != 2 && ! HAVE_C99_STRTOLD
|
|
65 # undef STRTOF
|
|
66 # define STRTOF strtod
|
|
67 #endif
|
|
68
|
|
69 /* On hosts where it's not known that snprintf works, use sprintf to
|
|
70 implement the subset needed here. Typically BUFSIZE is big enough
|
|
71 and there's little or no performance hit. */
|
|
72 #if ! GNULIB_SNPRINTF
|
|
73 # undef snprintf
|
|
74 # define snprintf ftoastr_snprintf
|
|
75 static int
|
|
76 ftoastr_snprintf (char *buf, size_t bufsize, char const *format,
|
|
77 int width, int prec, FLOAT x)
|
|
78 {
|
|
79 char width_0_buffer[LENGTH == 1 ? FLT_BUFSIZE_BOUND
|
|
80 : LENGTH == 2 ? DBL_BUFSIZE_BOUND
|
|
81 : LDBL_BUFSIZE_BOUND];
|
|
82 int n = width;
|
|
83 if (bufsize < sizeof width_0_buffer)
|
|
84 {
|
|
85 n = sprintf (width_0_buffer, format, 0, prec, x);
|
|
86 if (n < 0)
|
|
87 return n;
|
|
88 if (n < width)
|
|
89 n = width;
|
|
90 }
|
|
91 if (n < bufsize)
|
|
92 n = sprintf (buf, format, width, prec, x);
|
|
93 return n;
|
|
94 }
|
|
95 #endif
|
|
96
|
|
97 int
|
|
98 FTOASTR (char *buf, size_t bufsize, int flags, int width, FLOAT x)
|
|
99 {
|
|
100 /* The following method is simple but slow.
|
|
101 For ideas about speeding things up, please see:
|
|
102
|
|
103 Florian Loitsch, Printing floating-point numbers quickly and accurately
|
|
104 with integers. ACM SIGPLAN notices 46, 6 (June 2010), 233-243
|
|
105 <http://dx.doi.org/10.1145/1809028.1806623>; also see the
|
|
106 2010-03-21 draft <http://florian.loitsch.com/tmp/article.pdf>. */
|
|
107
|
|
108 char format[sizeof "%-+ 0*.*Lg"];
|
|
109 FLOAT abs_x = x < 0 ? -x : x;
|
|
110 int prec;
|
|
111
|
|
112 char *p = format;
|
|
113 *p++ = '%';
|
|
114
|
|
115 /* Support flags that generate output parsable by strtof. */
|
|
116 *p = '-'; p += (flags & FTOASTR_LEFT_JUSTIFY ) != 0;
|
|
117 *p = '+'; p += (flags & FTOASTR_ALWAYS_SIGNED ) != 0;
|
|
118 *p = ' '; p += (flags & FTOASTR_SPACE_POSITIVE) != 0;
|
|
119 *p = '0'; p += (flags & FTOASTR_ZERO_PAD ) != 0;
|
|
120
|
|
121 *p++ = '*';
|
|
122 *p++ = '.';
|
|
123 *p++ = '*';
|
|
124 *p = 'L'; p += 2 < LENGTH;
|
|
125 *p++ = flags & FTOASTR_UPPER_E ? 'G' : 'g';
|
|
126 *p = '\0';
|
|
127
|
|
128 for (prec = abs_x < FLOAT_MIN ? 1 : FLOAT_DIG; ; prec++)
|
|
129 {
|
|
130 int n = snprintf (buf, bufsize, format, width, prec, x);
|
|
131 if (n < 0
|
|
132 || FLOAT_PREC_BOUND <= prec
|
|
133 || (n < bufsize && STRTOF (buf, NULL) == x))
|
|
134 return n;
|
|
135 }
|
|
136 }
|