[BACK]Return to doprntf.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib / gmp / printf

Annotation of OpenXM_contrib/gmp/printf/doprntf.c, Revision 1.1.1.1

1.1       ohara       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 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 && 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: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>