beta-0.89.2
[luatex.git] / source / libs / gmp / gmp-src / printf / doprntf.c
blobc501e8d06b2fe00bfbf19cb524ee4208336a738b
1 /* __gmp_doprnt_mpf -- mpf formatted output.
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.
7 Copyright 2001, 2002, 2011 Free Software Foundation, Inc.
9 This file is part of the GNU MP Library.
11 The GNU MP Library is free software; you can redistribute it and/or modify
12 it under the terms of either:
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.
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.
24 or both in parallel, as here.
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.
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/. */
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>
41 #include "gmp.h"
42 #include "gmp-impl.h"
43 #include "longlong.h"
46 /* change this to "#define TRACE(x) x" for diagnostics */
47 #define TRACE(x)
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 */
53 #define DIGIT_VALUE(c) \
54 (isdigit (c) ? (c) - '0' \
55 : islower (c) ? (c) - 'a' + 10 \
56 : (c) - 'A' + 10)
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)
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;
74 TRACE (printf ("__gmp_doprnt_float\n");
75 printf (" conv=%d prec=%d\n", p->conv, p->prec));
77 prec = p->prec;
78 if (prec <= -1)
80 /* all digits */
81 ndigits = 0;
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));
88 else
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;
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;
111 default:
112 ASSERT (0);
113 /*FALLTHRU*/
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;
122 TRACE (printf (" ndigits %d\n", ndigits));
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));
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);
137 sign = p->sign;
138 if (s[0] == '-')
140 sign = s[0];
141 s++, len--;
143 signlen = (sign != '\0');
144 TRACE (printf (" sign %c signlen %d\n", sign, signlen));
146 switch (p->conv) {
147 case DOPRNT_CONV_FIXED:
148 if (prec <= -1)
149 prec = MAX (0, len-exp); /* retain all digits */
151 /* Truncate if necessary so fraction will be at most prec digits. */
152 ASSERT (prec >= 0);
153 newlen = exp + prec;
154 if (newlen < 0)
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;
161 else if (len <= newlen)
163 /* already got few enough digits */
165 else
167 /* discard excess digits and round to nearest */
169 const char *num_to_text = (p->base >= 0
170 ? "0123456789abcdefghijklmnopqrstuvwxyz"
171 : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
172 int base = ABS(p->base);
173 int n;
175 ASSERT (base <= 36);
177 len = newlen;
178 n = DIGIT_VALUE (s[len]);
179 TRACE (printf (" rounding with %d\n", n));
180 if (n >= (base + 1) / 2)
182 /* propagate a carry */
183 for (;;)
185 if (len == 0)
187 s[0] = '1';
188 len = 1;
189 exp++;
190 break;
192 n = DIGIT_VALUE (s[len-1]);
193 ASSERT (n >= 0 && n < base);
194 n++;
195 if (n != base)
197 TRACE (printf (" storing now %d\n", n));
198 s[len-1] = num_to_text[n];
199 break;
201 len--;
204 else
206 /* truncate only, strip any trailing zeros now exposed */
207 while (len > 0 && s[len-1] == '0')
208 len--;
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;
218 fixed:
219 ASSERT (len == 0 ? exp == 0 : 1);
220 if (exp <= 0)
222 TRACE (printf (" fixed 0.000sss\n"));
223 intlen = 0;
224 intzeros = 1;
225 fraczeros = -exp;
226 fraclen = len;
228 else
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;
236 explen = 0;
237 break;
239 case DOPRNT_CONV_SCIENTIFIC:
241 long int expval;
242 char expsign;
244 if (prec <= -1)
245 prec = MAX (0, len-1); /* retain all digits */
247 scientific:
248 TRACE (printf (" scientific s.sss\n"));
250 intlen = MIN (1, len);
251 intzeros = (intlen == 0 ? 1 : 0);
252 fraczeros = 0;
253 fraclen = len - intlen;
255 expval = (exp-intlen);
256 if (p->exptimes4)
257 expval <<= 2;
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);
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));
277 break;
279 default:
280 ASSERT (0);
281 /*FALLTHRU*/ /* to stop variables looking uninitialized */
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;
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));
301 if (p->showtrailing)
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);
310 else
311 preczeros = 0;
312 TRACE (printf (" prec=%d showtrailing=%d, pad with preczeros %d\n",
313 prec, p->showtrailing, preczeros));
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));
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;
340 break;
342 TRACE (printf (" showbase %s showbaselen %d\n",
343 showbase == NULL ? "" : showbase, showbaselen));
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));
350 justify = p->justify;
351 if (justlen <= 0) /* no justifying if exceed width */
352 justify = DOPRNT_JUSTIFY_NONE;
354 TRACE (printf (" justify type %d intlen %d pointlen %d fraclen %d\n",
355 justify, intlen, pointlen, fraclen));
357 if (justify == DOPRNT_JUSTIFY_RIGHT) /* pad for right */
358 DOPRNT_REPS (p->fill, justlen);
360 if (signlen) /* sign */
361 DOPRNT_REPS (sign, 1);
363 DOPRNT_MEMORY_MAYBE (showbase, showbaselen); /* base */
365 if (justify == DOPRNT_JUSTIFY_INTERNAL) /* pad for internal */
366 DOPRNT_REPS (p->fill, justlen);
368 DOPRNT_MEMORY (s, intlen); /* integer */
369 DOPRNT_REPS_MAYBE ('0', intzeros);
371 DOPRNT_MEMORY_MAYBE (point, pointlen); /* point */
373 DOPRNT_REPS_MAYBE ('0', fraczeros); /* frac */
374 DOPRNT_MEMORY_MAYBE (s+intlen, fraclen);
376 DOPRNT_REPS_MAYBE ('0', preczeros); /* prec */
378 DOPRNT_MEMORY_MAYBE (exponent, explen); /* exp */
380 if (justify == DOPRNT_JUSTIFY_LEFT) /* pad for left */
381 DOPRNT_REPS (p->fill, justlen);
383 done:
384 (*__gmp_free_func) (free_ptr, free_size);
385 return retval;
387 error:
388 retval = -1;
389 goto done;