sort(1): Fix some obvious issues
[dragonfly.git] / contrib / gmp / printf / doprntf.c
blob0a1928b26ecf09359faf8b2a57c85eb83a624e8d
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 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[GMP_LIMB_BITS + 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. Finally, we add 1 to
93 handle the case of 1-eps where EXP(f) = 0 but mpf_get_str returns
94 exp as 1. */
95 ndigits = prec + 2 + 1
96 + EXP(f) * (mp_bases[ABS(p->base)].chars_per_limb + (EXP(f)>=0));
97 ndigits = MAX (ndigits, 1);
98 break;
100 case DOPRNT_CONV_SCIENTIFIC:
101 /* precision is digits after the radix point, and there's one digit
102 before */
103 ndigits = prec + 1;
104 break;
106 default:
107 ASSERT (0);
108 /*FALLTHRU*/
110 case DOPRNT_CONV_GENERAL:
111 /* precision is total digits, but be sure to ask mpf_get_str for at
112 least 1, not 0 */
113 ndigits = MAX (prec, 1);
114 break;
117 TRACE (printf (" ndigits %d\n", ndigits));
119 s = mpf_get_str (NULL, &exp, p->base, ndigits, f);
120 len = strlen (s);
121 free_ptr = s;
122 free_size = len + 1;
123 TRACE (printf (" s %s\n", s);
124 printf (" exp %ld\n", exp);
125 printf (" len %d\n", len));
127 /* For fixed mode check the ndigits formed above was in fact enough for
128 the integer part plus p->prec after the radix point. */
129 ASSERT ((p->conv == DOPRNT_CONV_FIXED && p->prec > -1)
130 ? ndigits >= MAX (1, exp + p->prec + 2) : 1);
132 sign = p->sign;
133 if (s[0] == '-')
135 sign = s[0];
136 s++, len--;
138 signlen = (sign != '\0');
139 TRACE (printf (" sign %c signlen %d\n", sign, signlen));
141 switch (p->conv) {
142 case DOPRNT_CONV_FIXED:
143 if (prec <= -1)
144 prec = MAX (0, len-exp); /* retain all digits */
146 /* Truncate if necessary so fraction will be at most prec digits. */
147 ASSERT (prec >= 0);
148 newlen = exp + prec;
149 if (newlen < 0)
151 /* first non-zero digit is below target prec, and at least one zero
152 digit in between, so print zero */
153 len = 0;
154 exp = 0;
156 else if (len <= newlen)
158 /* already got few enough digits */
160 else
162 /* discard excess digits and round to nearest */
164 const char *num_to_text = (p->base >= 0
165 ? "0123456789abcdefghijklmnopqrstuvwxyz"
166 : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
167 int base = ABS(p->base);
168 int n;
170 ASSERT (base <= 36);
172 len = newlen;
173 n = DIGIT_VALUE (s[len]);
174 TRACE (printf (" rounding with %d\n", n));
175 if (n >= (base + 1) / 2)
177 /* propagate a carry */
178 for (;;)
180 if (len == 0)
182 s[0] = '1';
183 len = 1;
184 exp++;
185 break;
187 n = DIGIT_VALUE (s[len-1]);
188 ASSERT (n >= 0 && n < base);
189 n++;
190 if (n != base)
192 TRACE (printf (" storing now %d\n", n));
193 s[len-1] = num_to_text[n];
194 break;
196 len--;
199 else
201 /* truncate only, strip any trailing zeros now exposed */
202 while (len > 0 && s[len-1] == '0')
203 len--;
206 /* Can have newlen==0, in which case the truncate was just to check
207 for a carry turning it into "1". If we're left with len==0 then
208 adjust exp to match. */
209 if (len == 0)
210 exp = 0;
213 fixed:
214 ASSERT (len == 0 ? exp == 0 : 1);
215 if (exp <= 0)
217 TRACE (printf (" fixed 0.000sss\n"));
218 intlen = 0;
219 intzeros = 1;
220 fraczeros = -exp;
221 fraclen = len;
223 else
225 TRACE (printf (" fixed sss.sss or sss000\n"));
226 intlen = MIN (len, exp);
227 intzeros = exp - intlen;
228 fraczeros = 0;
229 fraclen = len - intlen;
231 explen = 0;
232 break;
234 case DOPRNT_CONV_SCIENTIFIC:
236 long int expval;
237 char expsign;
239 if (prec <= -1)
240 prec = MAX (0, len-1); /* retain all digits */
242 scientific:
243 TRACE (printf (" scientific s.sss\n"));
245 intlen = MIN (1, len);
246 intzeros = (intlen == 0 ? 1 : 0);
247 fraczeros = 0;
248 fraclen = len - intlen;
250 expval = (exp-intlen);
251 if (p->exptimes4)
252 expval <<= 2;
254 /* Split out the sign since %o or %x in expfmt give negatives as twos
255 complement, not with a sign. */
256 expsign = (expval >= 0 ? '+' : '-');
257 expval = ABS (expval);
259 #if HAVE_VSNPRINTF
260 explen = snprintf (exponent, sizeof(exponent),
261 p->expfmt, expsign, expval);
262 /* test for < sizeof-1 since a glibc 2.0.x return of sizeof-1 might
263 mean truncation */
264 ASSERT (explen >= 0 && explen < sizeof(exponent)-1);
265 #else
266 sprintf (exponent, p->expfmt, expsign, expval);
267 explen = strlen (exponent);
268 ASSERT (explen < sizeof(exponent));
269 #endif
270 TRACE (printf (" expfmt %s gives %s\n", p->expfmt, exponent));
272 break;
274 default:
275 ASSERT (0);
276 /*FALLTHRU*/ /* to stop variables looking uninitialized */
278 case DOPRNT_CONV_GENERAL:
279 /* The exponent for "scientific" will be exp-1, choose scientific if
280 this is < -4 or >= prec (and minimum 1 for prec). For f==0 will have
281 exp==0 and get the desired "fixed". This rule follows glibc. For
282 fixed there's no need to truncate, the desired ndigits will already
283 be as required. */
284 if (exp-1 < -4 || exp-1 >= MAX (1, prec))
285 goto scientific;
286 else
287 goto fixed;
290 TRACE (printf (" intlen %d intzeros %d fraczeros %d fraclen %d\n",
291 intlen, intzeros, fraczeros, fraclen));
292 ASSERT (p->prec <= -1
293 ? intlen + fraclen == strlen (s)
294 : intlen + fraclen <= strlen (s));
296 if (p->showtrailing)
298 /* Pad to requested precision with trailing zeros, for general this is
299 all digits, for fixed and scientific just the fraction. */
300 preczeros = prec - (fraczeros + fraclen
301 + (p->conv == DOPRNT_CONV_GENERAL
302 ? intlen + intzeros : 0));
303 preczeros = MAX (0, preczeros);
305 else
306 preczeros = 0;
307 TRACE (printf (" prec=%d showtrailing=%d, pad with preczeros %d\n",
308 prec, p->showtrailing, preczeros));
310 /* radix point if needed, or if forced */
311 pointlen = ((fraczeros + fraclen + preczeros) != 0 || p->showpoint != 0)
312 ? strlen (point) : 0;
313 TRACE (printf (" point |%s| pointlen %d\n", point, pointlen));
315 /* Notice the test for a non-zero value is done after any truncation for
316 DOPRNT_CONV_FIXED. */
317 showbase = NULL;
318 showbaselen = 0;
319 switch (p->showbase) {
320 default:
321 ASSERT (0);
322 /*FALLTHRU*/
323 case DOPRNT_SHOWBASE_NO:
324 break;
325 case DOPRNT_SHOWBASE_NONZERO:
326 if (intlen == 0 && fraclen == 0)
327 break;
328 /*FALLTHRU*/
329 case DOPRNT_SHOWBASE_YES:
330 switch (p->base) {
331 case 16: showbase = "0x"; showbaselen = 2; break;
332 case -16: showbase = "0X"; showbaselen = 2; break;
333 case 8: showbase = "0"; showbaselen = 1; break;
335 break;
337 TRACE (printf (" showbase %s showbaselen %d\n",
338 showbase == NULL ? "" : showbase, showbaselen));
340 /* left over field width */
341 justlen = p->width - (signlen + showbaselen + intlen + intzeros + pointlen
342 + fraczeros + fraclen + preczeros + explen);
343 TRACE (printf (" justlen %d fill 0x%X\n", justlen, p->fill));
345 justify = p->justify;
346 if (justlen <= 0) /* no justifying if exceed width */
347 justify = DOPRNT_JUSTIFY_NONE;
349 TRACE (printf (" justify type %d intlen %d pointlen %d fraclen %d\n",
350 justify, intlen, pointlen, fraclen));
352 if (justify == DOPRNT_JUSTIFY_RIGHT) /* pad for right */
353 DOPRNT_REPS (p->fill, justlen);
355 if (signlen) /* sign */
356 DOPRNT_REPS (sign, 1);
358 DOPRNT_MEMORY_MAYBE (showbase, showbaselen); /* base */
360 if (justify == DOPRNT_JUSTIFY_INTERNAL) /* pad for internal */
361 DOPRNT_REPS (p->fill, justlen);
363 DOPRNT_MEMORY (s, intlen); /* integer */
364 DOPRNT_REPS_MAYBE ('0', intzeros);
366 DOPRNT_MEMORY_MAYBE (point, pointlen); /* point */
368 DOPRNT_REPS_MAYBE ('0', fraczeros); /* frac */
369 DOPRNT_MEMORY_MAYBE (s+intlen, fraclen);
371 DOPRNT_REPS_MAYBE ('0', preczeros); /* prec */
373 DOPRNT_MEMORY_MAYBE (exponent, explen); /* exp */
375 if (justify == DOPRNT_JUSTIFY_LEFT) /* pad for left */
376 DOPRNT_REPS (p->fill, justlen);
378 done:
379 (*__gmp_free_func) (free_ptr, free_size);
380 return retval;
382 error:
383 retval = -1;
384 goto done;