Annotation of OpenXM_contrib/gmp/printf/doprntf.c, Revision 1.1.1.1
1.1 ohara 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 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 2.1 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; see the file COPYING.LIB. If not, write to
23: the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24: MA 02111-1307, USA. */
25:
26: #include "config.h"
27:
28: #if HAVE_STDARG
29: #include <stdarg.h> /* for va_list and hence doprnt_funs_t */
30: #else
31: #include <varargs.h>
32: #endif
33:
34: #include <ctype.h>
35: #include <string.h>
36: #include <stdio.h>
37: #include <stdlib.h>
38:
39: #if HAVE_LOCALE_H
40: #include <locale.h> /* for localeconv */
41: #endif
42:
43: #include "gmp.h"
44: #include "gmp-impl.h"
45:
46:
47: /* change this to "#define TRACE(x) x" for diagnostics */
48: #define TRACE(x)
49:
50:
51: /* The separate of __gmp_doprnt_float_digits and __gmp_doprnt_float is so
52: some C++ can do the mpf_get_str and release it in case of an exception */
53:
54: #define DIGIT_VALUE(c) \
55: (isdigit (c) ? (c) - '0' \
56: : islower (c) ? (c) - 'a' + 10 \
57: : (c) - 'A' + 10)
58:
59: int
60: __gmp_doprnt_mpf (const struct doprnt_funs_t *funs,
61: void *data,
62: const struct doprnt_params_t *p,
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[BITS_PER_MP_LIMB + 10];
71: const char *showbase;
72: const char *point;
73: int retval = 0;
74:
75: TRACE (printf ("__gmp_doprnt_float\n");
76: printf (" conv=%d prec=%d\n", p->conv, p->prec));
77:
78: prec = p->prec;
79: if (prec <= -1)
80: {
81: /* all digits */
82: ndigits = 0;
83:
84: /* arrange the fixed/scientific decision on a "prec" implied by how
85: many significant digits there are */
86: if (p->conv == DOPRNT_CONV_GENERAL)
87: MPF_SIGNIFICANT_DIGITS (prec, PREC(f), ABS(p->base));
88: }
89: else
90: {
91: switch (p->conv) {
92: case DOPRNT_CONV_FIXED:
93: /* Precision is digits after the radix point. Try not to generate
94: too many more than will actually be required. If f>=1 then
95: overestimate the integer part, and add prec. If f<1 then
96: underestimate the zeros between the radix point and the first
97: digit and subtract that from prec. In either case add 2 so the
98: round to nearest can be applied accurately. */
99: ndigits = prec + 2
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: 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: point = ".";
316: pointlen = ((fraczeros + fraclen + preczeros) != 0 || p->showpoint != 0);
317: #if HAVE_LOCALECONV
318: if (pointlen)
319: {
320: point = localeconv()->decimal_point;
321: pointlen = strlen (point);
322: }
323: #endif
324: TRACE (printf (" point |%s| pointlen %d\n", point, pointlen));
325:
326: /* Notice the test for a non-zero value is done after any truncation for
327: DOPRNT_CONV_FIXED. */
328: showbase = NULL;
329: showbaselen = 0;
330: switch (p->showbase) {
331: default:
332: ASSERT (0);
333: /*FALLTHRU*/
334: case DOPRNT_SHOWBASE_NO:
335: break;
336: case DOPRNT_SHOWBASE_NONZERO:
337: if (intlen == 0 && fraclen == 0)
338: break;
339: /*FALLTHRU*/
340: case DOPRNT_SHOWBASE_YES:
341: switch (p->base) {
342: case 16: showbase = "0x"; showbaselen = 2; break;
343: case -16: showbase = "0X"; showbaselen = 2; break;
344: case 8: showbase = "0"; showbaselen = 1; break;
345: }
346: break;
347: }
348: TRACE (printf (" showbase %s showbaselen %d\n",
349: showbase == NULL ? "" : showbase, showbaselen));
350:
351: /* left over field width */
352: justlen = p->width - (signlen + showbaselen + intlen + intzeros + pointlen
353: + fraczeros + fraclen + preczeros + explen);
354: TRACE (printf (" justlen %d fill 0x%X\n", justlen, p->fill));
355:
356: justify = p->justify;
357: if (justlen <= 0) /* no justifying if exceed width */
358: justify = DOPRNT_JUSTIFY_NONE;
359:
360: TRACE (printf (" justify type %d intlen %d pointlen %d fraclen %d\n",
361: justify, intlen, pointlen, fraclen));
362:
363: if (justify == DOPRNT_JUSTIFY_RIGHT) /* pad for right */
364: DOPRNT_REPS (p->fill, justlen);
365:
366: if (signlen) /* sign */
367: DOPRNT_REPS (sign, 1);
368:
369: DOPRNT_MEMORY_MAYBE (showbase, showbaselen); /* base */
370:
371: if (justify == DOPRNT_JUSTIFY_INTERNAL) /* pad for internal */
372: DOPRNT_REPS (p->fill, justlen);
373:
374: DOPRNT_MEMORY (s, intlen); /* integer */
375: DOPRNT_REPS_MAYBE ('0', intzeros);
376:
377: DOPRNT_MEMORY_MAYBE (point, pointlen); /* point */
378:
379: DOPRNT_REPS_MAYBE ('0', fraczeros); /* frac */
380: DOPRNT_MEMORY_MAYBE (s+intlen, fraclen);
381:
382: DOPRNT_REPS_MAYBE ('0', preczeros); /* prec */
383:
384: DOPRNT_MEMORY_MAYBE (exponent, explen); /* exp */
385:
386: if (justify == DOPRNT_JUSTIFY_LEFT) /* pad for left */
387: DOPRNT_REPS (p->fill, justlen);
388:
389: done:
390: (*__gmp_free_func) (free_ptr, free_size);
391: return retval;
392:
393: error:
394: retval = -1;
395: goto done;
396: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>