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   /* Now copy */
280 
281   done = -*decpt;
282 
283   while (p < end)
284     {
285       *fcvt_buf++ = *p++;
286       done++;
287     }
288   /* And unsuppress the trailing zeroes */
289   while (done < ndigit)
290     {
291       *fcvt_buf++ = '0';
292       done++;
293     }
294   *fcvt_buf++ = 0;
295   return save;
296 }
297 
298 char *
ecvtbuf(double invalue,int ndigit,int * decpt,int * sign,char * fcvt_buf)299 ecvtbuf (double invalue,
300 	int ndigit,
301 	int *decpt,
302 	int *sign,
303 	char *fcvt_buf)
304 {
305   char *save;
306   char *p;
307   char *end;
308   int done = 0;
309 
310   if (fcvt_buf == NULL)
311     {
312       if (_cvtlen <= ndigit)
313 	{
314 	  if ((fcvt_buf = (char *) realloc (_cvtbuf,
315 					    ndigit + 1)) == NULL)
316 	    return NULL;
317 	  _cvtlen = ndigit + 1;
318 	  _cvtbuf = fcvt_buf;
319 	}
320 
321       fcvt_buf = _cvtbuf ;
322     }
323 
324   save = fcvt_buf;
325 
326   p = __dtoa (invalue, 2, ndigit, decpt, sign, &end);
327   if (!p)
328     return NULL;
329 
330   /* Now copy */
331 
332   while (p < end)
333     {
334       *fcvt_buf++ = *p++;
335       done++;
336     }
337   /* And unsuppress the trailing zeroes */
338   while (done < ndigit)
339     {
340       *fcvt_buf++ = '0';
341       done++;
342     }
343   *fcvt_buf++ = 0;
344   return save;
345 }
346 
347 #endif
348 
349 char *
_gcvt(double invalue,int ndigit,char * buf,char type,int dot)350 _gcvt (
351 	double invalue,
352 	int ndigit,
353 	char *buf,
354 	char type,
355 	int dot)
356 {
357   char *save = buf;
358 
359   if (invalue < 0)
360     {
361       invalue = -invalue;
362     }
363 
364   if (invalue == 0)
365     {
366       *buf++ = '0';
367       *buf = '\0';
368     }
369   else
370     /* Which one to print ?
371        ANSI says that anything with more that 4 zeros after the . or more
372        than precision digits before is printed in e with the qualification
373        that trailing zeroes are removed from the fraction portion.  */
374 
375   if (0.0001 >= invalue || invalue >= _mprec_log10 (ndigit))
376     {
377       /* We subtract 1 from ndigit because in the 'e' format the precision is
378 	 the number of digits after the . but in 'g' format it is the number
379 	 of significant digits.
380 
381 	 We defer changing type to e/E so that print_e() can know it's us
382 	 calling and thus should remove trailing zeroes.  */
383 
384       print_e (buf, invalue, ndigit - 1, type, dot);
385     }
386   else
387     {
388       int decpt;
389       int sign;
390       char *end;
391       char *p;
392 
393       /* We always want ndigits of precision, even if that means printing
394        * a bunch of leading zeros for numbers < 1.0
395        */
396       p = __dtoa (invalue, 2, ndigit, &decpt, &sign, &end);
397       if (!p)
398 	return NULL;
399 
400       if (decpt == 9999)
401 	{
402 	  strcpy (buf, p);
403 	  return save;
404 	}
405       while (*p && decpt > 0)
406 	{
407 	  *buf++ = *p++;
408 	  decpt--;
409 	  ndigit--;
410 	}
411       /* Even if not in buffer */
412       while (decpt > 0 && ndigit > 0)
413 	{
414 	  *buf++ = '0';
415 	  decpt--;
416 	  ndigit--;
417 	}
418 
419       if (dot || *p)
420 	{
421 	  if (buf == save)
422 	    *buf++ = '0';
423 	  *buf++ = '.';
424 
425 	  /* Leading zeros don't count towards 'ndigit' */
426 	  while (decpt < 0)
427 	    {
428 	      *buf++ = '0';
429 	      decpt++;
430 	    }
431 
432 	  /* Print rest of stuff */
433 	  while (*p && ndigit > 0)
434 	    {
435 	      *buf++ = *p++;
436 	      ndigit--;
437 	    }
438 	  /* And trailing zeros */
439 	  if (dot)
440 	    {
441 	      while (ndigit > 0)
442 		{
443 		  *buf++ = '0';
444 		  ndigit--;
445 		}
446 	    }
447 	}
448       *buf++ = 0;
449     }
450 
451   return save;
452 }
453 
454 char *
_dcvt(char * buffer,double invalue,int precision,int width,char type,int dot)455 _dcvt (
456 	char *buffer,
457 	double invalue,
458 	int precision,
459 	int width,
460 	char type,
461 	int dot)
462 {
463   (void) width; /* XXX should be used */
464   switch (type)
465     {
466     case 'f':
467     case 'F':
468       print_f (buffer, invalue, precision, type, precision == 0 ? dot : 1, 3);
469       break;
470     case 'g':
471     case 'G':
472       if (precision == 0)
473 	precision = 1;
474       _gcvt (invalue, precision, buffer, type, dot);
475       break;
476     case 'e':
477     case 'E':
478       print_e (buffer, invalue, precision, type, dot);
479     }
480   return buffer;
481 }
482