doprntf.c
上传用户:qaz666999
上传日期:2022-08-06
资源大小:2570k
文件大小:10k
源码类别:

数学计算

开发平台:

Unix_Linux

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