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