remove .la files
[platform/upstream/gmp.git] / printf / doprntf.c
1 /* __gmp_doprnt_mpf -- mpf formatted output.
2
3    THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY.  THEY'RE ALMOST
4    CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
5    FUTURE GNU MP RELEASES.
6
7 Copyright 2001, 2002, 2011 Free Software Foundation, Inc.
8
9 This file is part of the GNU MP Library.
10
11 The GNU MP Library is free software; you can redistribute it and/or modify
12 it under the terms of either:
13
14   * the GNU Lesser General Public License as published by the Free
15     Software Foundation; either version 3 of the License, or (at your
16     option) any later version.
17
18 or
19
20   * the GNU General Public License as published by the Free Software
21     Foundation; either version 2 of the License, or (at your option) any
22     later version.
23
24 or both in parallel, as here.
25
26 The GNU MP Library is distributed in the hope that it will be useful, but
27 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
28 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
29 for more details.
30
31 You should have received copies of the GNU General Public License and the
32 GNU Lesser General Public License along with the GNU MP Library.  If not,
33 see https://www.gnu.org/licenses/.  */
34
35 #include <stdarg.h>    /* for va_list and hence doprnt_funs_t */
36 #include <ctype.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40
41 #include "gmp.h"
42 #include "gmp-impl.h"
43 #include "longlong.h"
44
45
46 /* change this to "#define TRACE(x) x" for diagnostics */
47 #define TRACE(x)
48
49
50 /* The separate of __gmp_doprnt_float_digits and __gmp_doprnt_float is so
51    some C++ can do the mpf_get_str and release it in case of an exception */
52
53 #define DIGIT_VALUE(c)                  \
54   (isdigit (c)   ? (c) - '0'            \
55    : islower (c) ? (c) - 'a' + 10       \
56    :               (c) - 'A' + 10)
57
58 int
59 __gmp_doprnt_mpf (const struct doprnt_funs_t *funs,
60                   void *data,
61                   const struct doprnt_params_t *p,
62                   const char *point,
63                   mpf_srcptr f)
64 {
65   int         prec, ndigits, free_size, len, newlen, justify, justlen, explen;
66   int         showbaselen, sign, signlen, intlen, intzeros, pointlen;
67   int         fraczeros, fraclen, preczeros;
68   char        *s, *free_ptr;
69   mp_exp_t    exp;
70   char        exponent[GMP_LIMB_BITS + 10];
71   const char  *showbase;
72   int         retval = 0;
73
74   TRACE (printf ("__gmp_doprnt_float\n");
75          printf ("  conv=%d prec=%d\n", p->conv, p->prec));
76
77   prec = p->prec;
78   if (prec <= -1)
79     {
80       /* all digits */
81       ndigits = 0;
82
83       /* arrange the fixed/scientific decision on a "prec" implied by how
84          many significant digits there are */
85       if (p->conv == DOPRNT_CONV_GENERAL)
86         MPF_SIGNIFICANT_DIGITS (prec, PREC(f), ABS(p->base));
87     }
88   else
89     {
90       switch (p->conv) {
91       case DOPRNT_CONV_FIXED:
92         /* Precision is digits after the radix point.  Try not to generate
93            too many more than will actually be required.  If f>=1 then
94            overestimate the integer part, and add prec.  If f<1 then
95            underestimate the zeros between the radix point and the first
96            digit and subtract that from prec.  In either case add 2 so the
97            round to nearest can be applied accurately.  Finally, we add 1 to
98            handle the case of 1-eps where EXP(f) = 0 but mpf_get_str returns
99            exp as 1.  */
100         ndigits = prec + 2 + 1
101           + EXP(f) * (mp_bases[ABS(p->base)].chars_per_limb + (EXP(f)>=0));
102         ndigits = MAX (ndigits, 1);
103         break;
104
105       case DOPRNT_CONV_SCIENTIFIC:
106         /* precision is digits after the radix point, and there's one digit
107            before */
108         ndigits = prec + 1;
109         break;
110
111       default:
112         ASSERT (0);
113         /*FALLTHRU*/
114
115       case DOPRNT_CONV_GENERAL:
116         /* precision is total digits, but be sure to ask mpf_get_str for at
117            least 1, not 0 */
118         ndigits = MAX (prec, 1);
119         break;
120       }
121     }
122   TRACE (printf ("  ndigits %d\n", ndigits));
123
124   s = mpf_get_str (NULL, &exp, p->base, ndigits, f);
125   len = strlen (s);
126   free_ptr = s;
127   free_size = len + 1;
128   TRACE (printf ("  s   %s\n", s);
129          printf ("  exp %ld\n", exp);
130          printf ("  len %d\n", len));
131
132   /* For fixed mode check the ndigits formed above was in fact enough for
133      the integer part plus p->prec after the radix point. */
134   ASSERT ((p->conv == DOPRNT_CONV_FIXED && p->prec > -1)
135           ? ndigits >= MAX (1, exp + p->prec + 2) : 1);
136
137   sign = p->sign;
138   if (s[0] == '-')
139     {
140       sign = s[0];
141       s++, len--;
142     }
143   signlen = (sign != '\0');
144   TRACE (printf ("  sign %c  signlen %d\n", sign, signlen));
145
146   switch (p->conv) {
147   case DOPRNT_CONV_FIXED:
148     if (prec <= -1)
149       prec = MAX (0, len-exp);   /* retain all digits */
150
151     /* Truncate if necessary so fraction will be at most prec digits. */
152     ASSERT (prec >= 0);
153     newlen = exp + prec;
154     if (newlen < 0)
155       {
156         /* first non-zero digit is below target prec, and at least one zero
157            digit in between, so print zero */
158         len = 0;
159         exp = 0;
160       }
161     else if (len <= newlen)
162       {
163         /* already got few enough digits */
164       }
165     else
166       {
167         /* discard excess digits and round to nearest */
168
169         const char  *num_to_text = (p->base >= 0
170                                     ? "0123456789abcdefghijklmnopqrstuvwxyz"
171                                     : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
172         int  base = ABS(p->base);
173         int  n;
174
175         ASSERT (base <= 36);
176
177         len = newlen;
178         n = DIGIT_VALUE (s[len]);
179         TRACE (printf ("  rounding with %d\n", n));
180         if (n >= (base + 1) / 2)
181           {
182             /* propagate a carry */
183             for (;;)
184               {
185                 if (len == 0)
186                   {
187                     s[0] = '1';
188                     len = 1;
189                     exp++;
190                     break;
191                   }
192                 n = DIGIT_VALUE (s[len-1]);
193                 ASSERT (n >= 0 && n < base);
194                 n++;
195                 if (n != base)
196                   {
197                     TRACE (printf ("  storing now %d\n", n));
198                     s[len-1] = num_to_text[n];
199                     break;
200                   }
201                 len--;
202               }
203           }
204         else
205           {
206             /* truncate only, strip any trailing zeros now exposed */
207             while (len > 0 && s[len-1] == '0')
208               len--;
209           }
210
211         /* Can have newlen==0, in which case the truncate was just to check
212            for a carry turning it into "1".  If we're left with len==0 then
213            adjust exp to match.  */
214         if (len == 0)
215           exp = 0;
216       }
217
218   fixed:
219     ASSERT (len == 0 ? exp == 0 : 1);
220     if (exp <= 0)
221       {
222         TRACE (printf ("  fixed 0.000sss\n"));
223         intlen = 0;
224         intzeros = 1;
225         fraczeros = -exp;
226         fraclen = len;
227       }
228     else
229       {
230         TRACE (printf ("  fixed sss.sss or sss000\n"));
231         intlen = MIN (len, exp);
232         intzeros = exp - intlen;
233         fraczeros = 0;
234         fraclen = len - intlen;
235       }
236     explen = 0;
237     break;
238
239   case DOPRNT_CONV_SCIENTIFIC:
240     {
241       long int expval;
242       char  expsign;
243
244       if (prec <= -1)
245         prec = MAX (0, len-1);   /* retain all digits */
246
247     scientific:
248       TRACE (printf ("  scientific s.sss\n"));
249
250       intlen = MIN (1, len);
251       intzeros = (intlen == 0 ? 1 : 0);
252       fraczeros = 0;
253       fraclen = len - intlen;
254
255       expval = (exp-intlen);
256       if (p->exptimes4)
257         expval <<= 2;
258
259       /* Split out the sign since %o or %x in expfmt give negatives as twos
260          complement, not with a sign. */
261       expsign = (expval >= 0 ? '+' : '-');
262       expval = ABS (expval);
263
264 #if HAVE_VSNPRINTF
265       explen = snprintf (exponent, sizeof(exponent),
266                          p->expfmt, expsign, expval);
267       /* test for < sizeof-1 since a glibc 2.0.x return of sizeof-1 might
268          mean truncation */
269       ASSERT (explen >= 0 && explen < sizeof(exponent)-1);
270 #else
271       sprintf (exponent, p->expfmt, expsign, expval);
272       explen = strlen (exponent);
273       ASSERT (explen < sizeof(exponent));
274 #endif
275       TRACE (printf ("  expfmt %s gives %s\n", p->expfmt, exponent));
276     }
277     break;
278
279   default:
280     ASSERT (0);
281     /*FALLTHRU*/  /* to stop variables looking uninitialized */
282
283   case DOPRNT_CONV_GENERAL:
284     /* The exponent for "scientific" will be exp-1, choose scientific if
285        this is < -4 or >= prec (and minimum 1 for prec).  For f==0 will have
286        exp==0 and get the desired "fixed".  This rule follows glibc.  For
287        fixed there's no need to truncate, the desired ndigits will already
288        be as required.  */
289     if (exp-1 < -4 || exp-1 >= MAX (1, prec))
290       goto scientific;
291     else
292       goto fixed;
293   }
294
295   TRACE (printf ("  intlen %d intzeros %d fraczeros %d fraclen %d\n",
296                  intlen, intzeros, fraczeros, fraclen));
297   ASSERT (p->prec <= -1
298           ? intlen + fraclen == strlen (s)
299           : intlen + fraclen <= strlen (s));
300
301   if (p->showtrailing)
302     {
303       /* Pad to requested precision with trailing zeros, for general this is
304          all digits, for fixed and scientific just the fraction.  */
305       preczeros = prec - (fraczeros + fraclen
306                           + (p->conv == DOPRNT_CONV_GENERAL
307                              ? intlen + intzeros : 0));
308       preczeros = MAX (0, preczeros);
309     }
310   else
311     preczeros = 0;
312   TRACE (printf ("  prec=%d showtrailing=%d, pad with preczeros %d\n",
313                  prec, p->showtrailing, preczeros));
314
315   /* radix point if needed, or if forced */
316   pointlen = ((fraczeros + fraclen + preczeros) != 0 || p->showpoint != 0)
317     ? strlen (point) : 0;
318   TRACE (printf ("  point |%s|  pointlen %d\n", point, pointlen));
319
320   /* Notice the test for a non-zero value is done after any truncation for
321      DOPRNT_CONV_FIXED. */
322   showbase = NULL;
323   showbaselen = 0;
324   switch (p->showbase) {
325   default:
326     ASSERT (0);
327     /*FALLTHRU*/
328   case DOPRNT_SHOWBASE_NO:
329     break;
330   case DOPRNT_SHOWBASE_NONZERO:
331     if (intlen == 0 && fraclen == 0)
332       break;
333     /*FALLTHRU*/
334   case DOPRNT_SHOWBASE_YES:
335     switch (p->base) {
336     case 16:  showbase = "0x"; showbaselen = 2; break;
337     case -16: showbase = "0X"; showbaselen = 2; break;
338     case 8:   showbase = "0";  showbaselen = 1; break;
339     }
340     break;
341   }
342   TRACE (printf ("  showbase %s showbaselen %d\n",
343                  showbase == NULL ? "" : showbase, showbaselen));
344
345   /* left over field width */
346   justlen = p->width - (signlen + showbaselen + intlen + intzeros + pointlen
347                         + fraczeros + fraclen + preczeros + explen);
348   TRACE (printf ("  justlen %d fill 0x%X\n", justlen, p->fill));
349
350   justify = p->justify;
351   if (justlen <= 0) /* no justifying if exceed width */
352     justify = DOPRNT_JUSTIFY_NONE;
353
354   TRACE (printf ("  justify type %d  intlen %d pointlen %d fraclen %d\n",
355                  justify, intlen, pointlen, fraclen));
356
357   if (justify == DOPRNT_JUSTIFY_RIGHT)         /* pad for right */
358     DOPRNT_REPS (p->fill, justlen);
359
360   if (signlen)                                 /* sign */
361     DOPRNT_REPS (sign, 1);
362
363   DOPRNT_MEMORY_MAYBE (showbase, showbaselen); /* base */
364
365   if (justify == DOPRNT_JUSTIFY_INTERNAL)      /* pad for internal */
366     DOPRNT_REPS (p->fill, justlen);
367
368   DOPRNT_MEMORY (s, intlen);                   /* integer */
369   DOPRNT_REPS_MAYBE ('0', intzeros);
370
371   DOPRNT_MEMORY_MAYBE (point, pointlen);       /* point */
372
373   DOPRNT_REPS_MAYBE ('0', fraczeros);          /* frac */
374   DOPRNT_MEMORY_MAYBE (s+intlen, fraclen);
375
376   DOPRNT_REPS_MAYBE ('0', preczeros);          /* prec */
377
378   DOPRNT_MEMORY_MAYBE (exponent, explen);      /* exp */
379
380   if (justify == DOPRNT_JUSTIFY_LEFT)          /* pad for left */
381     DOPRNT_REPS (p->fill, justlen);
382
383  done:
384   (*__gmp_free_func) (free_ptr, free_size);
385   return retval;
386
387  error:
388   retval = -1;
389   goto done;
390 }