Commit | Line | Data |
---|---|---|
a451f14b PE |
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 | |
8c9b2106 PE |
43 | # if HAVE_C99_STRTOLD |
44 | # define STRTOF strtold | |
45 | # endif | |
a451f14b PE |
46 | #elif LENGTH == 2 |
47 | # define FLOAT double | |
48 | # define FLOAT_DIG DBL_DIG | |
49 | # define FLOAT_MIN DBL_MIN | |
50 | # define FLOAT_PREC_BOUND _GL_DBL_PREC_BOUND | |
51 | # define FTOASTR dtoastr | |
a451f14b PE |
52 | #else |
53 | # define LENGTH 1 | |
54 | # define FLOAT float | |
55 | # define FLOAT_DIG FLT_DIG | |
56 | # define FLOAT_MIN FLT_MIN | |
57 | # define FLOAT_PREC_BOUND _GL_FLT_PREC_BOUND | |
58 | # define FTOASTR ftoastr | |
8c9b2106 PE |
59 | # if HAVE_STRTOF |
60 | # define STRTOF strtof | |
61 | # endif | |
a451f14b PE |
62 | #endif |
63 | ||
64 | /* On pre-C99 hosts, approximate strtof and strtold with strtod. This | |
65 | may generate one or two extra digits, but that's better than not | |
8c9b2106 PE |
66 | working at all. */ |
67 | #ifndef STRTOF | |
a451f14b PE |
68 | # define STRTOF strtod |
69 | #endif | |
70 | ||
71 | /* On hosts where it's not known that snprintf works, use sprintf to | |
72 | implement the subset needed here. Typically BUFSIZE is big enough | |
73 | and there's little or no performance hit. */ | |
74 | #if ! GNULIB_SNPRINTF | |
75 | # undef snprintf | |
76 | # define snprintf ftoastr_snprintf | |
77 | static int | |
78 | ftoastr_snprintf (char *buf, size_t bufsize, char const *format, | |
79 | int width, int prec, FLOAT x) | |
80 | { | |
81 | char width_0_buffer[LENGTH == 1 ? FLT_BUFSIZE_BOUND | |
82 | : LENGTH == 2 ? DBL_BUFSIZE_BOUND | |
83 | : LDBL_BUFSIZE_BOUND]; | |
84 | int n = width; | |
85 | if (bufsize < sizeof width_0_buffer) | |
86 | { | |
87 | n = sprintf (width_0_buffer, format, 0, prec, x); | |
88 | if (n < 0) | |
89 | return n; | |
90 | if (n < width) | |
91 | n = width; | |
92 | } | |
93 | if (n < bufsize) | |
94 | n = sprintf (buf, format, width, prec, x); | |
95 | return n; | |
96 | } | |
97 | #endif | |
98 | ||
99 | int | |
100 | FTOASTR (char *buf, size_t bufsize, int flags, int width, FLOAT x) | |
101 | { | |
102 | /* The following method is simple but slow. | |
103 | For ideas about speeding things up, please see: | |
104 | ||
105 | Florian Loitsch, Printing floating-point numbers quickly and accurately | |
106 | with integers. ACM SIGPLAN notices 46, 6 (June 2010), 233-243 | |
107 | <http://dx.doi.org/10.1145/1809028.1806623>; also see the | |
108 | 2010-03-21 draft <http://florian.loitsch.com/tmp/article.pdf>. */ | |
109 | ||
110 | char format[sizeof "%-+ 0*.*Lg"]; | |
111 | FLOAT abs_x = x < 0 ? -x : x; | |
112 | int prec; | |
113 | ||
114 | char *p = format; | |
115 | *p++ = '%'; | |
116 | ||
117 | /* Support flags that generate output parsable by strtof. */ | |
118 | *p = '-'; p += (flags & FTOASTR_LEFT_JUSTIFY ) != 0; | |
119 | *p = '+'; p += (flags & FTOASTR_ALWAYS_SIGNED ) != 0; | |
120 | *p = ' '; p += (flags & FTOASTR_SPACE_POSITIVE) != 0; | |
121 | *p = '0'; p += (flags & FTOASTR_ZERO_PAD ) != 0; | |
122 | ||
123 | *p++ = '*'; | |
124 | *p++ = '.'; | |
125 | *p++ = '*'; | |
126 | *p = 'L'; p += 2 < LENGTH; | |
127 | *p++ = flags & FTOASTR_UPPER_E ? 'G' : 'g'; | |
128 | *p = '\0'; | |
129 | ||
130 | for (prec = abs_x < FLOAT_MIN ? 1 : FLOAT_DIG; ; prec++) | |
131 | { | |
132 | int n = snprintf (buf, bufsize, format, width, prec, x); | |
133 | if (n < 0 | |
134 | || FLOAT_PREC_BOUND <= prec | |
135 | || (n < bufsize && STRTOF (buf, NULL) == x)) | |
136 | return n; | |
137 | } | |
138 | } |