source: trunk/third/gmp/printf/doprntf.c @ 18191

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