~ubuntu-branches/ubuntu/quantal/gclcvs/quantal

« back to all changes in this revision

Viewing changes to gmp3/printf/doprntf.c

  • Committer: Bazaar Package Importer
  • Author(s): Camm Maguire
  • Date: 2004-06-24 15:13:46 UTC
  • Revision ID: james.westby@ubuntu.com-20040624151346-xh0xaaktyyp7aorc
Tags: 2.7.0-26
C_GC_OFFSET is 2 on m68k-linux

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 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 2.1 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; see the file COPYING.LIB.  If not, write to
 
23
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 
24
MA 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
 
 
59
int
 
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)
 
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
}