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 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 2.1 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; 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. */
29
#include <stdarg.h> /* for va_list and hence doprnt_funs_t */
40
#include <locale.h> /* for localeconv */
47
/* change this to "#define TRACE(x) x" for diagnostics */
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 */
54
#define DIGIT_VALUE(c) \
55
(isdigit (c) ? (c) - '0' \
56
: islower (c) ? (c) - 'a' + 10 \
60
__gmp_doprnt_mpf (const struct doprnt_funs_t *funs,
62
const struct doprnt_params_t *p,
65
int prec, ndigits, free_size, len, newlen, justify, justlen, explen;
66
int showbaselen, sign, signlen, intlen, intzeros, pointlen;
67
int fraczeros, fraclen, preczeros;
70
char exponent[BITS_PER_MP_LIMB + 10];
75
TRACE (printf ("__gmp_doprnt_float\n");
76
printf (" conv=%d prec=%d\n", p->conv, p->prec));
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));
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. */
100
+ EXP(f) * (__mp_bases[ABS(p->base)].chars_per_limb + (EXP(f)>=0));
101
ndigits = MAX (ndigits, 1);
104
case DOPRNT_CONV_SCIENTIFIC:
105
/* precision is digits after the radix point, and there's one digit
114
case DOPRNT_CONV_GENERAL:
115
/* precision is total digits, but be sure to ask mpf_get_str for at
117
ndigits = MAX (prec, 1);
121
TRACE (printf (" ndigits %d\n", ndigits));
123
s = mpf_get_str (NULL, &exp, p->base, ndigits, f);
127
TRACE (printf (" s %s\n", s);
128
printf (" exp %ld\n", exp);
129
printf (" len %d\n", len));
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);
142
signlen = (sign != '\0');
143
TRACE (printf (" sign %c signlen %d\n", sign, signlen));
146
case DOPRNT_CONV_FIXED:
148
prec = MAX (0, len-exp); /* retain all digits */
150
/* Truncate if necessary so fraction will be at most prec digits. */
155
/* first non-zero digit is below target prec, and at least one zero
156
digit in between, so print zero */
160
else if (len <= newlen)
162
/* already got few enough digits */
166
/* discard excess digits and round to nearest */
168
const char *num_to_text = (p->base >= 0
169
? "0123456789abcdefghijklmnopqrstuvwxyz"
170
: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
171
int base = ABS(p->base);
177
n = DIGIT_VALUE (s[len]);
178
TRACE (printf (" rounding with %d\n", n));
179
if (n >= (base + 1) / 2)
181
/* propagate a carry */
191
n = DIGIT_VALUE (s[len-1]);
192
ASSERT (n >= 0 && n < base);
196
TRACE (printf (" storing now %d\n", n));
197
s[len-1] = num_to_text[n];
205
/* truncate only, strip any trailing zeros now exposed */
206
while (len > 0 && s[len-1] == '0')
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. */
218
ASSERT (len == 0 ? exp == 0 : 1);
221
TRACE (printf (" fixed 0.000sss\n"));
229
TRACE (printf (" fixed sss.sss or sss000\n"));
230
intlen = MIN (len, exp);
231
intzeros = exp - intlen;
233
fraclen = len - intlen;
238
case DOPRNT_CONV_SCIENTIFIC:
244
prec = MAX (0, len-1); /* retain all digits */
247
TRACE (printf (" scientific s.sss\n"));
249
intlen = MIN (1, len);
250
intzeros = (intlen == 0 ? 1 : 0);
252
fraclen = len - intlen;
254
expval = (exp-intlen);
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);
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
268
ASSERT (explen >= 0 && explen < sizeof(exponent)-1);
270
sprintf (exponent, p->expfmt, expsign, expval);
271
explen = strlen (exponent);
272
ASSERT (explen < sizeof(exponent));
274
TRACE (printf (" expfmt %s gives %s\n", p->expfmt, exponent));
280
/*FALLTHRU*/ /* to stop variables looking uninitialized */
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
288
if (exp-1 < -4 || exp-1 >= MAX (1, prec))
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));
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);
311
TRACE (printf (" prec=%d showtrailing=%d, pad with preczeros %d\n",
312
prec, p->showtrailing, preczeros));
314
/* radix point if needed, or if forced */
316
pointlen = ((fraczeros + fraclen + preczeros) != 0 || p->showpoint != 0);
320
point = localeconv()->decimal_point;
321
pointlen = strlen (point);
324
TRACE (printf (" point |%s| pointlen %d\n", point, pointlen));
326
/* Notice the test for a non-zero value is done after any truncation for
327
DOPRNT_CONV_FIXED. */
330
switch (p->showbase) {
334
case DOPRNT_SHOWBASE_NO:
336
case DOPRNT_SHOWBASE_NONZERO:
340
case DOPRNT_SHOWBASE_YES:
342
case 16: showbase = "0x"; showbaselen = 2; break;
343
case -16: showbase = "0X"; showbaselen = 2; break;
344
case 8: showbase = "0"; showbaselen = 1; break;
348
TRACE (printf (" showbase %s showbaselen %d\n",
349
showbase == NULL ? "" : showbase, showbaselen));
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));
356
justify = p->justify;
357
if (justlen <= 0) /* no justifying if exceed width */
358
justify = DOPRNT_JUSTIFY_NONE;
360
TRACE (printf (" justify type %d intlen %d pointlen %d fraclen %d\n",
361
justify, intlen, pointlen, fraclen));
363
if (justify == DOPRNT_JUSTIFY_RIGHT) /* pad for right */
364
DOPRNT_REPS (p->fill, justlen);
366
if (signlen) /* sign */
367
DOPRNT_REPS (sign, 1);
369
DOPRNT_MEMORY_MAYBE (showbase, showbaselen); /* base */
371
if (justify == DOPRNT_JUSTIFY_INTERNAL) /* pad for internal */
372
DOPRNT_REPS (p->fill, justlen);
374
DOPRNT_MEMORY (s, intlen); /* integer */
375
DOPRNT_REPS_MAYBE ('0', intzeros);
377
DOPRNT_MEMORY_MAYBE (point, pointlen); /* point */
379
DOPRNT_REPS_MAYBE ('0', fraczeros); /* frac */
380
DOPRNT_MEMORY_MAYBE (s+intlen, fraclen);
382
DOPRNT_REPS_MAYBE ('0', preczeros); /* prec */
384
DOPRNT_MEMORY_MAYBE (exponent, explen); /* exp */
386
if (justify == DOPRNT_JUSTIFY_LEFT) /* pad for left */
387
DOPRNT_REPS (p->fill, justlen);
390
(*__gmp_free_func) (free_ptr, free_size);