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/. */
27 #include <stdarg.h> /* for va_list and hence doprnt_funs_t */
41 /* change this to "#define TRACE(x) x" for diagnostics */
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 \
54 __gmp_doprnt_mpf (const struct doprnt_funs_t
*funs
,
56 const struct doprnt_params_t
*p
,
60 int prec
, ndigits
, free_size
, len
, newlen
, justify
, justlen
, explen
;
61 int showbaselen
, sign
, signlen
, intlen
, intzeros
, pointlen
;
62 int fraczeros
, fraclen
, preczeros
;
65 char exponent
[GMP_LIMB_BITS
+ 10];
69 TRACE (printf ("__gmp_doprnt_float\n");
70 printf (" conv=%d prec=%d\n", p
->conv
, p
->prec
));
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
));
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
95 ndigits
= prec
+ 2 + 1
96 + EXP(f
) * (mp_bases
[ABS(p
->base
)].chars_per_limb
+ (EXP(f
)>=0));
97 ndigits
= MAX (ndigits
, 1);
100 case DOPRNT_CONV_SCIENTIFIC
:
101 /* precision is digits after the radix point, and there's one digit
110 case DOPRNT_CONV_GENERAL
:
111 /* precision is total digits, but be sure to ask mpf_get_str for at
113 ndigits
= MAX (prec
, 1);
117 TRACE (printf (" ndigits %d\n", ndigits
));
119 s
= mpf_get_str (NULL
, &exp
, p
->base
, ndigits
, f
);
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);
138 signlen
= (sign
!= '\0');
139 TRACE (printf (" sign %c signlen %d\n", sign
, signlen
));
142 case DOPRNT_CONV_FIXED
:
144 prec
= MAX (0, len
-exp
); /* retain all digits */
146 /* Truncate if necessary so fraction will be at most prec digits. */
151 /* first non-zero digit is below target prec, and at least one zero
152 digit in between, so print zero */
156 else if (len
<= newlen
)
158 /* already got few enough digits */
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
);
173 n
= DIGIT_VALUE (s
[len
]);
174 TRACE (printf (" rounding with %d\n", n
));
175 if (n
>= (base
+ 1) / 2)
177 /* propagate a carry */
187 n
= DIGIT_VALUE (s
[len
-1]);
188 ASSERT (n
>= 0 && n
< base
);
192 TRACE (printf (" storing now %d\n", n
));
193 s
[len
-1] = num_to_text
[n
];
201 /* truncate only, strip any trailing zeros now exposed */
202 while (len
> 0 && s
[len
-1] == '0')
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. */
214 ASSERT (len
== 0 ? exp
== 0 : 1);
217 TRACE (printf (" fixed 0.000sss\n"));
225 TRACE (printf (" fixed sss.sss or sss000\n"));
226 intlen
= MIN (len
, exp
);
227 intzeros
= exp
- intlen
;
229 fraclen
= len
- intlen
;
234 case DOPRNT_CONV_SCIENTIFIC
:
240 prec
= MAX (0, len
-1); /* retain all digits */
243 TRACE (printf (" scientific s.sss\n"));
245 intlen
= MIN (1, len
);
246 intzeros
= (intlen
== 0 ? 1 : 0);
248 fraclen
= len
- intlen
;
250 expval
= (exp
-intlen
);
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
);
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
264 ASSERT (explen
>= 0 && explen
< sizeof(exponent
)-1);
266 sprintf (exponent
, p
->expfmt
, expsign
, expval
);
267 explen
= strlen (exponent
);
268 ASSERT (explen
< sizeof(exponent
));
270 TRACE (printf (" expfmt %s gives %s\n", p
->expfmt
, exponent
));
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
284 if (exp
-1 < -4 || exp
-1 >= MAX (1, prec
))
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
));
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
);
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. */
319 switch (p
->showbase
) {
323 case DOPRNT_SHOWBASE_NO
:
325 case DOPRNT_SHOWBASE_NONZERO
:
326 if (intlen
== 0 && fraclen
== 0)
329 case DOPRNT_SHOWBASE_YES
:
331 case 16: showbase
= "0x"; showbaselen
= 2; break;
332 case -16: showbase
= "0X"; showbaselen
= 2; break;
333 case 8: showbase
= "0"; showbaselen
= 1; 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
);
379 (*__gmp_free_func
) (free_ptr
, free_size
);