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