1 /*
2 Copyright (c) 1990 Regents of the University of California.
3 All rights reserved.
4 */
5 /*
6 FUNCTION
7 <<ecvtbuf>>, <<fcvtbuf>>---double or float to string
8
9 INDEX
10 ecvtbuf
11 INDEX
12 fcvtbuf
13
14 SYNOPSIS
15 #include <stdio.h>
16
17 char *ecvtbuf(double <[val]>, int <[chars]>, int *<[decpt]>,
18 int *<[sgn]>, char *<[buf]>);
19
20 char *fcvtbuf(double <[val]>, int <[decimals]>, int *<[decpt]>,
21 int *<[sgn]>, char *<[buf]>);
22
23 DESCRIPTION
24 <<ecvtbuf>> and <<fcvtbuf>> produce (null-terminated) strings
25 of digits representating the <<double>> number <[val]>.
26
27 The only difference between <<ecvtbuf>> and <<fcvtbuf>> is the
28 interpretation of the second argument (<[chars]> or
29 <[decimals]>). For <<ecvtbuf>>, the second argument <[chars]>
30 specifies the total number of characters to write (which is
31 also the number of significant digits in the formatted string,
32 since these two functions write only digits). For <<fcvtbuf>>,
33 the second argument <[decimals]> specifies the number of
34 characters to write after the decimal point; all digits for
35 the integer part of <[val]> are always included.
36
37 Since <<ecvtbuf>> and <<fcvtbuf>> write only digits in the
38 output string, they record the location of the decimal point
39 in <<*<[decpt]>>>, and the sign of the number in <<*<[sgn]>>>.
40 After formatting a number, <<*<[decpt]>>> contains the number
41 of digits to the left of the decimal point. <<*<[sgn]>>>
42 contains <<0>> if the number is positive, and <<1>> if it is
43 negative. For both functions, you supply a pointer <[buf]> to
44 an area of memory to hold the converted string.
45
46 RETURNS
47 Both functions return a pointer to <[buf]>, the string
48 containing a character representation of <[val]>.
49
50 PORTABILITY
51 Neither function is ANSI C.
52
53 Supporting OS subroutines required: <<close>>, <<fstat>>, <<isatty>>,
54 <<lseek>>, <<read>>, <<sbrk>>, <<write>>.
55 */
56
57 #define _DEFAULT_SOURCE
58 #include <_ansi.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include "mprec.h"
62 #include "local.h"
63 #include "atexit.h"
64
65 static void
print_f(char * buf,double invalue,int ndigit,char type,int dot,int mode)66 print_f (
67 char *buf,
68 double invalue,
69 int ndigit,
70 char type,
71 int dot,
72 int mode)
73 {
74 int decpt;
75 int sign;
76 char *p, *start, *end;
77
78 (void) type; /* XXX should be used */
79 start = p = __dtoa (invalue, mode, ndigit, &decpt, &sign, &end);
80 if (!p) {
81 buf[0] = '\0';
82 return;
83 }
84
85 if (decpt == 9999)
86 {
87 strcpy (buf, p);
88 return;
89 }
90 while (*p && decpt > 0)
91 {
92 *buf++ = *p++;
93 decpt--;
94 }
95 /* Even if not in buffer */
96 while (decpt > 0)
97 {
98 *buf++ = '0';
99 decpt--;
100 }
101
102 if (dot || *p)
103 {
104 if (p == start)
105 *buf++ = '0';
106 if (decpt < 0 && ndigit > 0)
107 *buf++ = '.';
108 while (decpt < 0 && ndigit > 0)
109 {
110 *buf++ = '0';
111 decpt++;
112 ndigit--;
113 }
114
115 /* Print rest of stuff */
116 while (*p && ndigit > 0)
117 {
118 *buf++ = *p++;
119 ndigit--;
120 }
121 /* And trailing zeros */
122 while (ndigit > 0)
123 {
124 *buf++ = '0';
125 ndigit--;
126 }
127 }
128 *buf++ = 0;
129 }
130
131 /* Print number in e format with width chars after.
132
133 TYPE is one of 'e' or 'E'. It may also be one of 'g' or 'G' indicating
134 that _gcvt is calling us and we should remove trailing zeroes.
135
136 WIDTH is the number of digits of precision after the decimal point. */
137
138 static void
print_e(char * buf,double invalue,int width,char type,int dot)139 print_e (
140 char *buf,
141 double invalue,
142 int width,
143 char type,
144 int dot)
145 {
146 int sign;
147 char *end;
148 char *p;
149 int decpt;
150 int top;
151 int ndigit = width;
152
153 p = __dtoa (invalue, 2, width + 1, &decpt, &sign, &end);
154 if (!p) {
155 buf[0] = '\0';
156 return;
157 }
158
159 if (decpt == 9999)
160 {
161 strcpy (buf, p);
162 return;
163 }
164
165 *buf++ = *p++;
166 if (ndigit > 0)
167 dot = 1;
168
169 while (*p && ndigit > 0)
170 {
171 if (dot) {
172 *buf++ = '.';
173 dot = 0;
174 }
175 *buf++ = *p++;
176 ndigit--;
177 }
178
179 /* Add trailing zeroes to fill out to ndigits unless this is 'g' format.
180 Also, convert g/G to e/E. */
181
182 if (type == 'g')
183 type = 'e';
184 else if (type == 'G')
185 type = 'E';
186 else
187 {
188 while (ndigit > 0)
189 {
190 if (dot) {
191 *buf++ = '.';
192 dot = 0;
193 }
194 *buf++ = '0';
195 ndigit--;
196 }
197 }
198
199 /* Add the exponent. */
200
201 *buf++ = type;
202 decpt--;
203 if (decpt < 0)
204 {
205 *buf++ = '-';
206 decpt = -decpt;
207 }
208 else
209 {
210 *buf++ = '+';
211 }
212 if (decpt > 99)
213 {
214 int top = decpt / 100;
215 *buf++ = top + '0';
216 decpt -= top * 100;
217 }
218 top = decpt / 10;
219 *buf++ = top + '0';
220 decpt -= top * 10;
221 *buf++ = decpt + '0';
222
223 *buf++ = 0;
224 }
225
226 #ifndef _REENT_ONLY
227
228 static NEWLIB_THREAD_LOCAL int _cvtlen;
229 static NEWLIB_THREAD_LOCAL char *_cvtbuf;
230
231 static void
_cvtcleanup(void)232 _cvtcleanup(void)
233 {
234 if (_cvtbuf) {
235 free(_cvtbuf);
236 _cvtbuf = NULL;
237 }
238 }
239
240 /* Undocumented behaviour: when given NULL as a buffer, return a
241 pointer to static space in the rent structure. This is only to
242 support ecvt and fcvt, which aren't ANSI anyway. */
243
244 char *
fcvtbuf(double invalue,int ndigit,int * decpt,int * sign,char * fcvt_buf)245 fcvtbuf (double invalue,
246 int ndigit,
247 int *decpt,
248 int *sign,
249 char *fcvt_buf)
250 {
251 char *save;
252 char *p;
253 char *end;
254 int done = 0;
255
256 if (fcvt_buf == NULL)
257 {
258 if (_cvtlen <= ndigit + 35)
259 {
260 if (!_cvtbuf)
261 if (atexit(_cvtcleanup) != 0)
262 return NULL;
263 if ((fcvt_buf = (char *) realloc (_cvtbuf,
264 ndigit + 36)) == NULL)
265 return NULL;
266 _cvtlen = ndigit + 36;
267 _cvtbuf = fcvt_buf;
268 }
269
270 fcvt_buf = _cvtbuf ;
271 }
272
273 save = fcvt_buf;
274
275 p = __dtoa (invalue, 3, ndigit, decpt, sign, &end);
276 if (!p)
277 return NULL;
278
279 if (*decpt == 9999)
280 {
281 strcpy(fcvt_buf, p);
282 return fcvt_buf;
283 }
284
285 /* Now copy */
286
287 done = -*decpt;
288
289 while (p < end)
290 {
291 *fcvt_buf++ = *p++;
292 done++;
293 }
294 /* And unsuppress the trailing zeroes */
295 while (done < ndigit)
296 {
297 *fcvt_buf++ = '0';
298 done++;
299 }
300 *fcvt_buf++ = 0;
301 return save;
302 }
303
304 char *
ecvtbuf(double invalue,int ndigit,int * decpt,int * sign,char * fcvt_buf)305 ecvtbuf (double invalue,
306 int ndigit,
307 int *decpt,
308 int *sign,
309 char *fcvt_buf)
310 {
311 char *save;
312 char *p;
313 char *end;
314 int done = 0;
315
316 if (fcvt_buf == NULL)
317 {
318 if (_cvtlen <= ndigit)
319 {
320 if ((fcvt_buf = (char *) realloc (_cvtbuf,
321 ndigit + 1)) == NULL)
322 return NULL;
323 _cvtlen = ndigit + 1;
324 _cvtbuf = fcvt_buf;
325 }
326
327 fcvt_buf = _cvtbuf ;
328 }
329
330 save = fcvt_buf;
331
332 p = __dtoa (invalue, 2, ndigit, decpt, sign, &end);
333 if (!p)
334 return NULL;
335
336 if (*decpt == 9999)
337 {
338 strcpy(fcvt_buf, p);
339 return fcvt_buf;
340 }
341
342 /* Now copy */
343
344 while (p < end)
345 {
346 *fcvt_buf++ = *p++;
347 done++;
348 }
349 /* And unsuppress the trailing zeroes */
350 while (done < ndigit)
351 {
352 *fcvt_buf++ = '0';
353 done++;
354 }
355 *fcvt_buf++ = 0;
356 return save;
357 }
358
359 #endif
360
361 char *
_gcvt(double invalue,int ndigit,char * buf,char type,int dot)362 _gcvt (
363 double invalue,
364 int ndigit,
365 char *buf,
366 char type,
367 int dot)
368 {
369 char *save = buf;
370
371 if (invalue < 0)
372 {
373 invalue = -invalue;
374 }
375
376 if (invalue == 0)
377 {
378 *buf++ = '0';
379 *buf = '\0';
380 }
381 else
382 /* Which one to print ?
383 ANSI says that anything with more that 4 zeros after the . or more
384 than precision digits before is printed in e with the qualification
385 that trailing zeroes are removed from the fraction portion. */
386
387 if (0.0001 >= invalue || invalue >= _mprec_log10 (ndigit))
388 {
389 /* We subtract 1 from ndigit because in the 'e' format the precision is
390 the number of digits after the . but in 'g' format it is the number
391 of significant digits.
392
393 We defer changing type to e/E so that print_e() can know it's us
394 calling and thus should remove trailing zeroes. */
395
396 print_e (buf, invalue, ndigit - 1, type, dot);
397 }
398 else
399 {
400 int decpt;
401 int sign;
402 char *end;
403 char *p;
404
405 /* We always want ndigits of precision, even if that means printing
406 * a bunch of leading zeros for numbers < 1.0
407 */
408 p = __dtoa (invalue, 2, ndigit, &decpt, &sign, &end);
409 if (!p)
410 return NULL;
411
412 if (decpt == 9999)
413 {
414 strcpy (buf, p);
415 return save;
416 }
417 while (*p && decpt > 0)
418 {
419 *buf++ = *p++;
420 decpt--;
421 ndigit--;
422 }
423 /* Even if not in buffer */
424 while (decpt > 0 && ndigit > 0)
425 {
426 *buf++ = '0';
427 decpt--;
428 ndigit--;
429 }
430
431 if (dot || *p)
432 {
433 if (buf == save)
434 *buf++ = '0';
435 *buf++ = '.';
436
437 /* Leading zeros don't count towards 'ndigit' */
438 while (decpt < 0)
439 {
440 *buf++ = '0';
441 decpt++;
442 }
443
444 /* Print rest of stuff */
445 while (*p && ndigit > 0)
446 {
447 *buf++ = *p++;
448 ndigit--;
449 }
450 /* And trailing zeros */
451 if (dot)
452 {
453 while (ndigit > 0)
454 {
455 *buf++ = '0';
456 ndigit--;
457 }
458 }
459 }
460 *buf++ = 0;
461 }
462
463 return save;
464 }
465
466 char *
_dcvt(char * buffer,double invalue,int precision,int width,char type,int dot)467 _dcvt (
468 char *buffer,
469 double invalue,
470 int precision,
471 int width,
472 char type,
473 int dot)
474 {
475 (void) width; /* XXX should be used */
476 switch (type)
477 {
478 case 'f':
479 case 'F':
480 print_f (buffer, invalue, precision, type, precision == 0 ? dot : 1, 3);
481 break;
482 case 'g':
483 case 'G':
484 if (precision == 0)
485 precision = 1;
486 _gcvt (invalue, precision, buffer, type, dot);
487 break;
488 case 'e':
489 case 'E':
490 print_e (buffer, invalue, precision, type, dot);
491 }
492 return buffer;
493 }
494