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