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 _GNU_SOURCE
58 #include <stdlib.h>
59 #include <string.h>
60 #include "mprec.h"
61 #include "local.h"
62 #include "atexit.h"
63 
64 /* Print number in e format with width chars after.
65 
66    TYPE is one of 'e' or 'E'.  It may also be one of 'g' or 'G' indicating
67    that _gcvt is calling us and we should remove trailing zeroes.
68 
69    WIDTH is the number of digits of precision after the decimal point.  */
70 
71 static void
print_e(char * buf,double invalue,int width,char type,int dot)72 print_e (
73 	char *buf,
74 	double invalue,
75 	int width,
76 	char type,
77 	int dot)
78 {
79   int sign;
80   char *end;
81   char *p;
82   int decpt;
83   int top;
84   int ndigit = width;
85 
86   p = __dtoa (invalue, 2, width + 1, &decpt, &sign, &end);
87   if (!p) {
88     buf[0] = '\0';
89     return;
90   }
91 
92   if (decpt == 9999)
93     {
94       strcpy (buf, p);
95       return;
96     }
97 
98   *buf++ = *p++;
99   if (ndigit > 0)
100     dot = 1;
101 
102   while (*p && ndigit > 0)
103     {
104       if (dot) {
105 	*buf++ = '.';
106 	dot = 0;
107       }
108       *buf++ = *p++;
109       ndigit--;
110     }
111 
112   /* Add trailing zeroes to fill out to ndigits unless this is 'g' format.
113      Also, convert g/G to e/E.  */
114 
115   if (type == 'g')
116     type = 'e';
117   else if (type == 'G')
118     type = 'E';
119   else
120     {
121       while (ndigit > 0)
122 	{
123 	  if  (dot) {
124 	    *buf++ = '.';
125 	    dot = 0;
126 	  }
127 	  *buf++ = '0';
128 	  ndigit--;
129 	}
130     }
131 
132   /* Add the exponent.  */
133 
134   *buf++ = type;
135   decpt--;
136   if (decpt < 0)
137     {
138       *buf++ = '-';
139       decpt = -decpt;
140     }
141   else
142     {
143       *buf++ = '+';
144     }
145   if (decpt > 99)
146     {
147       int top = decpt / 100;
148       *buf++ = top + '0';
149       decpt -= top * 100;
150     }
151   top = decpt / 10;
152   *buf++ = top + '0';
153   decpt -= top * 10;
154   *buf++ = decpt + '0';
155 
156   *buf++ = 0;
157 }
158 
159 #ifndef _REENT_ONLY
160 
161 static NEWLIB_THREAD_LOCAL int _cvtlen;
162 static NEWLIB_THREAD_LOCAL char *_cvtbuf;
163 
164 static void
_cvtcleanup(void)165 _cvtcleanup(void)
166 {
167   if (_cvtbuf) {
168     free(_cvtbuf);
169     _cvtbuf = NULL;
170   }
171 }
172 
173 /* Undocumented behaviour: when given NULL as a buffer, return a
174    pointer to static space in the rent structure.  This is only to
175    support ecvt and fcvt, which aren't ANSI anyway.  */
176 
177 char *
fcvtbuf(double invalue,int ndigit,int * decpt,int * sign,char * fcvt_buf)178 fcvtbuf (double invalue,
179 	int ndigit,
180 	int *decpt,
181 	int *sign,
182 	char *fcvt_buf)
183 {
184   char *save;
185   char *p;
186   char *end;
187   int done = 0;
188 
189   if (fcvt_buf == NULL)
190     {
191       if (_cvtlen <= ndigit + 35)
192 	{
193 	  if  (!_cvtbuf)
194 	    if (atexit(_cvtcleanup) != 0)
195 	      return NULL;
196 	  if ((fcvt_buf = (char *) realloc (_cvtbuf,
197 					    ndigit + 36)) == NULL)
198 	    return NULL;
199 	  _cvtlen = ndigit + 36;
200 	  _cvtbuf = fcvt_buf;
201 	}
202 
203       fcvt_buf = _cvtbuf ;
204     }
205 
206   save = fcvt_buf;
207 
208   p = __dtoa (invalue, 3, ndigit, decpt, sign, &end);
209   if (!p)
210     return NULL;
211 
212   if (*decpt == 9999)
213     {
214       strcpy(fcvt_buf, p);
215       return fcvt_buf;
216     }
217 
218   /* Now copy */
219 
220   done = -*decpt;
221 
222   while (p < end)
223     {
224       *fcvt_buf++ = *p++;
225       done++;
226     }
227   /* And unsuppress the trailing zeroes */
228   while (done < ndigit)
229     {
230       *fcvt_buf++ = '0';
231       done++;
232     }
233   *fcvt_buf++ = 0;
234   return save;
235 }
236 
237 char *
ecvtbuf(double invalue,int ndigit,int * decpt,int * sign,char * fcvt_buf)238 ecvtbuf (double invalue,
239 	int ndigit,
240 	int *decpt,
241 	int *sign,
242 	char *fcvt_buf)
243 {
244   char *save;
245   char *p;
246   char *end;
247   int done = 0;
248 
249   if (fcvt_buf == NULL)
250     {
251       if (_cvtlen <= ndigit)
252 	{
253 	  if ((fcvt_buf = (char *) realloc (_cvtbuf,
254 					    ndigit + 1)) == NULL)
255 	    return NULL;
256 	  _cvtlen = ndigit + 1;
257 	  _cvtbuf = fcvt_buf;
258 	}
259 
260       fcvt_buf = _cvtbuf ;
261     }
262 
263   save = fcvt_buf;
264 
265   p = __dtoa (invalue, 2, ndigit, decpt, sign, &end);
266   if (!p)
267     return NULL;
268 
269   if (*decpt == 9999)
270     {
271       strcpy(fcvt_buf, p);
272       return fcvt_buf;
273     }
274 
275   /* Now copy */
276 
277   while (p < end)
278     {
279       *fcvt_buf++ = *p++;
280       done++;
281     }
282   /* And unsuppress the trailing zeroes */
283   while (done < ndigit)
284     {
285       *fcvt_buf++ = '0';
286       done++;
287     }
288   *fcvt_buf++ = 0;
289   return save;
290 }
291 
292 #endif
293 
294 char *
_gcvt(double invalue,int ndigit,char * buf,char type,int dot)295 _gcvt (
296 	double invalue,
297 	int ndigit,
298 	char *buf,
299 	char type,
300 	int dot)
301 {
302   char *save = buf;
303 
304   if (invalue < 0)
305     {
306       invalue = -invalue;
307     }
308 
309   if (invalue == 0)
310     {
311       *buf++ = '0';
312       *buf = '\0';
313     }
314   else
315     /* Which one to print ?
316        ANSI says that anything with more that 4 zeros after the . or more
317        than precision digits before is printed in e with the qualification
318        that trailing zeroes are removed from the fraction portion.  */
319 
320   if (0.0001 >= invalue || invalue >= _mprec_log10 (ndigit))
321     {
322       /* We subtract 1 from ndigit because in the 'e' format the precision is
323 	 the number of digits after the . but in 'g' format it is the number
324 	 of significant digits.
325 
326 	 We defer changing type to e/E so that print_e() can know it's us
327 	 calling and thus should remove trailing zeroes.  */
328 
329       print_e (buf, invalue, ndigit - 1, type, dot);
330     }
331   else
332     {
333       int decpt;
334       int sign;
335       char *end;
336       char *p;
337 
338       /* We always want ndigits of precision, even if that means printing
339        * a bunch of leading zeros for numbers < 1.0
340        */
341       p = __dtoa (invalue, 2, ndigit, &decpt, &sign, &end);
342       if (!p)
343 	return NULL;
344 
345       if (decpt == 9999)
346 	{
347 	  strcpy (buf, p);
348 	  return save;
349 	}
350       while (*p && decpt > 0)
351 	{
352 	  *buf++ = *p++;
353 	  decpt--;
354 	  ndigit--;
355 	}
356       /* Even if not in buffer */
357       while (decpt > 0 && ndigit > 0)
358 	{
359 	  *buf++ = '0';
360 	  decpt--;
361 	  ndigit--;
362 	}
363 
364       if (dot || *p)
365 	{
366 	  if (buf == save)
367 	    *buf++ = '0';
368 	  *buf++ = '.';
369 
370 	  /* Leading zeros don't count towards 'ndigit' */
371 	  while (decpt < 0)
372 	    {
373 	      *buf++ = '0';
374 	      decpt++;
375 	    }
376 
377 	  /* Print rest of stuff */
378 	  while (*p && ndigit > 0)
379 	    {
380 	      *buf++ = *p++;
381 	      ndigit--;
382 	    }
383 	  /* And trailing zeros */
384 	  if (dot)
385 	    {
386 	      while (ndigit > 0)
387 		{
388 		  *buf++ = '0';
389 		  ndigit--;
390 		}
391 	    }
392 	}
393       *buf++ = 0;
394     }
395 
396   return save;
397 }
398