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 }