1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include "fmtdef.h"
5
6 enum
7 {
8 FDIGIT = 30,
9 FDEFLT = 6,
10 NSIGNIF = 17,
11 NEXP10 = 308,
12 };
13
14 static int
xadd(char * a,int n,int v)15 xadd(char *a, int n, int v)
16 {
17 char *b;
18 int c;
19
20 if(n < 0 || n >= NSIGNIF)
21 return 0;
22 for(b = a+n; b >= a; b--) {
23 c = *b + v;
24 if(c <= '9') {
25 *b = c;
26 return 0;
27 }
28 *b = '0';
29 v = 1;
30 }
31 *a = '1'; // overflow adding
32 return 1;
33 }
34
35 static int
xsub(char * a,int n,int v)36 xsub(char *a, int n, int v)
37 {
38 char *b;
39 int c;
40
41 for(b = a+n; b >= a; b--) {
42 c = *b - v;
43 if(c >= '0') {
44 *b = c;
45 return 0;
46 }
47 *b = '9';
48 v = 1;
49 }
50 *a = '9'; // underflow subtracting
51 return 1;
52 }
53
54 static void
xdtoa(Fmt * fmt,char * s2,double f)55 xdtoa(Fmt *fmt, char *s2, double f)
56 {
57 char s1[NSIGNIF+10];
58 double g, h;
59 int e, d, i, n;
60 int c1, c2, c3, c4, ucase, sign, chr, prec;
61
62 prec = FDEFLT;
63 if(fmt->flags & FmtPrec)
64 prec = fmt->prec;
65 if(prec > FDIGIT)
66 prec = FDIGIT;
67 if(isNaN(f)) {
68 strcpy(s2, "NaN");
69 return;
70 }
71 if(isInf(f, 1)) {
72 strcpy(s2, "+Inf");
73 return;
74 }
75 if(isInf(f, -1)) {
76 strcpy(s2, "-Inf");
77 return;
78 }
79 sign = 0;
80 if(f < 0) {
81 f = -f;
82 sign++;
83 }
84 ucase = 0;
85 chr = fmt->r;
86 if(isupper(chr)) {
87 ucase = 1;
88 chr = tolower(chr);
89 }
90
91 e = 0;
92 g = f;
93 if(g != 0) {
94 frexp(f, &e);
95 e = e * .301029995664;
96 if(e >= -150 && e <= +150) {
97 d = 0;
98 h = f;
99 } else {
100 d = e/2;
101 h = f * pow10(-d);
102 }
103 g = h * pow10(d-e);
104 while(g < 1) {
105 e--;
106 g = h * pow10(d-e);
107 }
108 while(g >= 10) {
109 e++;
110 g = h * pow10(d-e);
111 }
112 }
113
114 /*
115 * convert NSIGNIF digits and convert
116 * back to get accuracy.
117 */
118 for(i=0; i<NSIGNIF; i++) {
119 d = g;
120 s1[i] = d + '0';
121 g = (g - d) * 10;
122 }
123 s1[i] = 0;
124
125 /*
126 * try decimal rounding to eliminate 9s
127 */
128 c2 = prec + 1;
129 if(chr == 'f')
130 c2 += e;
131 if(c2 >= NSIGNIF-2) {
132 strcpy(s2, s1);
133 d = e;
134 s1[NSIGNIF-2] = '0';
135 s1[NSIGNIF-1] = '0';
136 sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
137 g = strtod(s1, nil);
138 if(g == f)
139 goto found;
140 if(xadd(s1, NSIGNIF-3, 1)) {
141 e++;
142 sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
143 }
144 g = strtod(s1, nil);
145 if(g == f)
146 goto found;
147 strcpy(s1, s2);
148 e = d;
149 }
150
151 /*
152 * convert back so s1 gets exact answer
153 */
154 for(;;) {
155 sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
156 g = strtod(s1, nil);
157 if(f > g) {
158 if(xadd(s1, NSIGNIF-1, 1))
159 e--;
160 continue;
161 }
162 if(f < g) {
163 if(xsub(s1, NSIGNIF-1, 1))
164 e++;
165 continue;
166 }
167 break;
168 }
169
170 found:
171 /*
172 * sign
173 */
174 d = 0;
175 i = 0;
176 if(sign)
177 s2[d++] = '-';
178 else if(fmt->flags & FmtSign)
179 s2[d++] = '+';
180 else if(fmt->flags & FmtSpace)
181 s2[d++] = ' ';
182
183 /*
184 * copy into final place
185 * c1 digits of leading '0'
186 * c2 digits from conversion
187 * c3 digits of trailing '0'
188 * c4 digits after '.'
189 */
190 c1 = 0;
191 c2 = prec + 1;
192 c3 = 0;
193 c4 = prec;
194 switch(chr) {
195 default:
196 if(xadd(s1, c2, 5))
197 e++;
198 break;
199 case 'g':
200 /*
201 * decide on 'e' of 'f' style convers
202 */
203 if(xadd(s1, c2, 5))
204 e++;
205 if(e >= -5 && e <= prec) {
206 c1 = -e - 1;
207 c4 = prec - e;
208 chr = 'h'; // flag for 'f' style
209 }
210 break;
211 case 'f':
212 if(xadd(s1, c2+e, 5))
213 e++;
214 c1 = -e;
215 if(c1 > prec)
216 c1 = c2;
217 c2 += e;
218 break;
219 }
220
221 /*
222 * clean up c1 c2 and c3
223 */
224 if(c1 < 0)
225 c1 = 0;
226 if(c2 < 0)
227 c2 = 0;
228 if(c2 > NSIGNIF) {
229 c3 = c2-NSIGNIF;
230 c2 = NSIGNIF;
231 }
232
233 /*
234 * copy digits
235 */
236 while(c1 > 0) {
237 if(c1+c2+c3 == c4)
238 s2[d++] = '.';
239 s2[d++] = '0';
240 c1--;
241 }
242 while(c2 > 0) {
243 if(c2+c3 == c4)
244 s2[d++] = '.';
245 s2[d++] = s1[i++];
246 c2--;
247 }
248 while(c3 > 0) {
249 if(c3 == c4)
250 s2[d++] = '.';
251 s2[d++] = '0';
252 c3--;
253 }
254
255 /*
256 * strip trailing '0' on g conv
257 */
258 if(fmt->flags & FmtSharp) {
259 if(0 == c4)
260 s2[d++] = '.';
261 } else
262 if(chr == 'g' || chr == 'h') {
263 for(n=d-1; n>=0; n--)
264 if(s2[n] != '0')
265 break;
266 for(i=n; i>=0; i--)
267 if(s2[i] == '.') {
268 d = n;
269 if(i != n)
270 d++;
271 break;
272 }
273 }
274 if(chr == 'e' || chr == 'g') {
275 if(ucase)
276 s2[d++] = 'E';
277 else
278 s2[d++] = 'e';
279 c1 = e;
280 if(c1 < 0) {
281 s2[d++] = '-';
282 c1 = -c1;
283 } else
284 s2[d++] = '+';
285 if(c1 >= 100) {
286 s2[d++] = c1/100 + '0';
287 c1 = c1%100;
288 }
289 s2[d++] = c1/10 + '0';
290 s2[d++] = c1%10 + '0';
291 }
292 s2[d] = 0;
293 }
294
295 int
_floatfmt(Fmt * fmt,double f)296 _floatfmt(Fmt *fmt, double f)
297 {
298 char s[1+NEXP10+1+FDIGIT+1];
299
300 /*
301 * The max length of a %f string is
302 * '[+-]'+"max exponent"+'.'+"max precision"+'\0'
303 * which is 341 currently.
304 */
305 xdtoa(fmt, s, f);
306 fmt->flags &= FmtWidth|FmtLeft;
307 _fmtcpy(fmt, s, strlen(s), strlen(s));
308 return 0;
309 }
310
311 int
_efgfmt(Fmt * f)312 _efgfmt(Fmt *f)
313 {
314 double d;
315
316 d = va_arg(f->args, double);
317 return _floatfmt(f, d);
318 }
319