github.com/aergoio/aergo@v1.3.1/libtool/src/gmp-6.1.2/printf/doprntf.c (about)

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