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