(root)/
gmp-6.3.0/
printf/
doprntf.c
       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-impl.h"
      42  #include "longlong.h"
      43  
      44  
      45  /* change this to "#define TRACE(x) x" for diagnostics */
      46  #define TRACE(x)
      47  
      48  
      49  /* The separate of __gmp_doprnt_float_digits and __gmp_doprnt_float is so
      50     some C++ can do the mpf_get_str and release it in case of an exception */
      51  
      52  #define DIGIT_VALUE(c)                  \
      53    (isdigit (c)   ? (c) - '0'            \
      54     : islower (c) ? (c) - 'a' + 10       \
      55     :               (c) - 'A' + 10)
      56  
      57  int
      58  __gmp_doprnt_mpf (const struct doprnt_funs_t *funs,
      59  		  void *data,
      60  		  const struct doprnt_params_t *p,
      61  		  const char *point,
      62  		  mpf_srcptr f)
      63  {
      64    int         prec, ndigits, free_size, len, newlen, justify, justlen, explen;
      65    int         showbaselen, sign, signlen, intlen, intzeros, pointlen;
      66    int         fraczeros, fraclen, preczeros;
      67    char        *s, *free_ptr;
      68    mp_exp_t    exp;
      69    char        exponent[GMP_LIMB_BITS + 10];
      70    const char  *showbase;
      71    int         retval = 0;
      72  
      73    TRACE (printf ("__gmp_doprnt_float\n");
      74  	 printf ("  conv=%d prec=%d\n", p->conv, p->prec));
      75  
      76    prec = p->prec;
      77    if (prec <= -1)
      78      {
      79        /* all digits */
      80        ndigits = 0;
      81  
      82        /* arrange the fixed/scientific decision on a "prec" implied by how
      83  	 many significant digits there are */
      84        if (p->conv == DOPRNT_CONV_GENERAL)
      85  	MPF_SIGNIFICANT_DIGITS (prec, PREC(f), ABS(p->base));
      86      }
      87    else
      88      {
      89        switch (p->conv) {
      90        case DOPRNT_CONV_FIXED:
      91  	/* Precision is digits after the radix point.  Try not to generate
      92  	   too many more than will actually be required.  If f>=1 then
      93  	   overestimate the integer part, and add prec.  If f<1 then
      94  	   underestimate the zeros between the radix point and the first
      95  	   digit and subtract that from prec.  In either case add 2 so the
      96  	   round to nearest can be applied accurately.  Finally, we add 1 to
      97  	   handle the case of 1-eps where EXP(f) = 0 but mpf_get_str returns
      98  	   exp as 1.  */
      99  	ndigits = prec + 2 + 1
     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        long 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    pointlen = ((fraczeros + fraclen + preczeros) != 0 || p->showpoint != 0)
     316      ? strlen (point) : 0;
     317    TRACE (printf ("  point |%s|  pointlen %d\n", point, pointlen));
     318  
     319    /* Notice the test for a non-zero value is done after any truncation for
     320       DOPRNT_CONV_FIXED. */
     321    showbase = NULL;
     322    showbaselen = 0;
     323    switch (p->showbase) {
     324    default:
     325      ASSERT (0);
     326      /*FALLTHRU*/
     327    case DOPRNT_SHOWBASE_NO:
     328      break;
     329    case DOPRNT_SHOWBASE_NONZERO:
     330      if (intlen == 0 && fraclen == 0)
     331        break;
     332      /*FALLTHRU*/
     333    case DOPRNT_SHOWBASE_YES:
     334      switch (p->base) {
     335      case 16:  showbase = "0x"; showbaselen = 2; break;
     336      case -16: showbase = "0X"; showbaselen = 2; break;
     337      case 8:   showbase = "0";  showbaselen = 1; break;
     338      }
     339      break;
     340    }
     341    TRACE (printf ("  showbase %s showbaselen %d\n",
     342  		 showbase == NULL ? "" : showbase, showbaselen));
     343  
     344    /* left over field width */
     345    justlen = p->width - (signlen + showbaselen + intlen + intzeros + pointlen
     346  			+ fraczeros + fraclen + preczeros + explen);
     347    TRACE (printf ("  justlen %d fill 0x%X\n", justlen, p->fill));
     348  
     349    justify = p->justify;
     350    if (justlen <= 0) /* no justifying if exceed width */
     351      justify = DOPRNT_JUSTIFY_NONE;
     352  
     353    TRACE (printf ("  justify type %d  intlen %d pointlen %d fraclen %d\n",
     354  		 justify, intlen, pointlen, fraclen));
     355  
     356    if (justify == DOPRNT_JUSTIFY_RIGHT)         /* pad for right */
     357      DOPRNT_REPS (p->fill, justlen);
     358  
     359    if (signlen)                                 /* sign */
     360      DOPRNT_REPS (sign, 1);
     361  
     362    DOPRNT_MEMORY_MAYBE (showbase, showbaselen); /* base */
     363  
     364    if (justify == DOPRNT_JUSTIFY_INTERNAL)      /* pad for internal */
     365      DOPRNT_REPS (p->fill, justlen);
     366  
     367    DOPRNT_MEMORY (s, intlen);                   /* integer */
     368    DOPRNT_REPS_MAYBE ('0', intzeros);
     369  
     370    DOPRNT_MEMORY_MAYBE (point, pointlen);       /* point */
     371  
     372    DOPRNT_REPS_MAYBE ('0', fraczeros);          /* frac */
     373    DOPRNT_MEMORY_MAYBE (s+intlen, fraclen);
     374  
     375    DOPRNT_REPS_MAYBE ('0', preczeros);          /* prec */
     376  
     377    DOPRNT_MEMORY_MAYBE (exponent, explen);      /* exp */
     378  
     379    if (justify == DOPRNT_JUSTIFY_LEFT)          /* pad for left */
     380      DOPRNT_REPS (p->fill, justlen);
     381  
     382   done:
     383    __GMP_FREE_FUNC_TYPE (free_ptr, free_size, char);
     384    return retval;
     385  
     386   error:
     387    retval = -1;
     388    goto done;
     389  }