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