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 either:
14 * the GNU Lesser General Public License as published by the Free
15 Software Foundation; either version 3 of the License, or (at your
16 option) any later version.
20 * the GNU General Public License as published by the Free Software
21 Foundation; either version 2 of the License, or (at your option) any
24 or both in parallel, as here.
26 The GNU MP Library is distributed in the hope that it will be useful, but
27 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
28 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
31 You should have received copies of the GNU General Public License and the
32 GNU Lesser General Public License along with the GNU MP Library. If not,
33 see https://www.gnu.org/licenses/. */
35 #include <stdarg.h> /* for va_list and hence doprnt_funs_t */
46 /* change this to "#define TRACE(x) x" for diagnostics */
50 /* The separate of __gmp_doprnt_float_digits and __gmp_doprnt_float is so
51 some C++ can do the mpf_get_str and release it in case of an exception */
53 #define DIGIT_VALUE(c) \
54 (isdigit (c) ? (c) - '0' \
55 : islower (c) ? (c) - 'a' + 10 \
59 __gmp_doprnt_mpf (const struct doprnt_funs_t
*funs
,
61 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
[GMP_LIMB_BITS
+ 10];
74 TRACE (printf ("__gmp_doprnt_float\n");
75 printf (" conv=%d prec=%d\n", p
->conv
, p
->prec
));
83 /* arrange the fixed/scientific decision on a "prec" implied by how
84 many significant digits there are */
85 if (p
->conv
== DOPRNT_CONV_GENERAL
)
86 MPF_SIGNIFICANT_DIGITS (prec
, PREC(f
), ABS(p
->base
));
91 case DOPRNT_CONV_FIXED
:
92 /* Precision is digits after the radix point. Try not to generate
93 too many more than will actually be required. If f>=1 then
94 overestimate the integer part, and add prec. If f<1 then
95 underestimate the zeros between the radix point and the first
96 digit and subtract that from prec. In either case add 2 so the
97 round to nearest can be applied accurately. Finally, we add 1 to
98 handle the case of 1-eps where EXP(f) = 0 but mpf_get_str returns
100 ndigits
= prec
+ 2 + 1
101 + EXP(f
) * (mp_bases
[ABS(p
->base
)].chars_per_limb
+ (EXP(f
)>=0));
102 ndigits
= MAX (ndigits
, 1);
105 case DOPRNT_CONV_SCIENTIFIC
:
106 /* precision is digits after the radix point, and there's one digit
115 case DOPRNT_CONV_GENERAL
:
116 /* precision is total digits, but be sure to ask mpf_get_str for at
118 ndigits
= MAX (prec
, 1);
122 TRACE (printf (" ndigits %d\n", ndigits
));
124 s
= mpf_get_str (NULL
, &exp
, p
->base
, ndigits
, f
);
128 TRACE (printf (" s %s\n", s
);
129 printf (" exp %ld\n", exp
);
130 printf (" len %d\n", len
));
132 /* For fixed mode check the ndigits formed above was in fact enough for
133 the integer part plus p->prec after the radix point. */
134 ASSERT ((p
->conv
== DOPRNT_CONV_FIXED
&& p
->prec
> -1)
135 ? ndigits
>= MAX (1, exp
+ p
->prec
+ 2) : 1);
143 signlen
= (sign
!= '\0');
144 TRACE (printf (" sign %c signlen %d\n", sign
, signlen
));
147 case DOPRNT_CONV_FIXED
:
149 prec
= MAX (0, len
-exp
); /* retain all digits */
151 /* Truncate if necessary so fraction will be at most prec digits. */
156 /* first non-zero digit is below target prec, and at least one zero
157 digit in between, so print zero */
161 else if (len
<= newlen
)
163 /* already got few enough digits */
167 /* discard excess digits and round to nearest */
169 const char *num_to_text
= (p
->base
>= 0
170 ? "0123456789abcdefghijklmnopqrstuvwxyz"
171 : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
172 int base
= ABS(p
->base
);
178 n
= DIGIT_VALUE (s
[len
]);
179 TRACE (printf (" rounding with %d\n", n
));
180 if (n
>= (base
+ 1) / 2)
182 /* propagate a carry */
192 n
= DIGIT_VALUE (s
[len
-1]);
193 ASSERT (n
>= 0 && n
< base
);
197 TRACE (printf (" storing now %d\n", n
));
198 s
[len
-1] = num_to_text
[n
];
206 /* truncate only, strip any trailing zeros now exposed */
207 while (len
> 0 && s
[len
-1] == '0')
211 /* Can have newlen==0, in which case the truncate was just to check
212 for a carry turning it into "1". If we're left with len==0 then
213 adjust exp to match. */
219 ASSERT (len
== 0 ? exp
== 0 : 1);
222 TRACE (printf (" fixed 0.000sss\n"));
230 TRACE (printf (" fixed sss.sss or sss000\n"));
231 intlen
= MIN (len
, exp
);
232 intzeros
= exp
- intlen
;
234 fraclen
= len
- intlen
;
239 case DOPRNT_CONV_SCIENTIFIC
:
245 prec
= MAX (0, len
-1); /* retain all digits */
248 TRACE (printf (" scientific s.sss\n"));
250 intlen
= MIN (1, len
);
251 intzeros
= (intlen
== 0 ? 1 : 0);
253 fraclen
= len
- intlen
;
255 expval
= (exp
-intlen
);
259 /* Split out the sign since %o or %x in expfmt give negatives as twos
260 complement, not with a sign. */
261 expsign
= (expval
>= 0 ? '+' : '-');
262 expval
= ABS (expval
);
265 explen
= snprintf (exponent
, sizeof(exponent
),
266 p
->expfmt
, expsign
, expval
);
267 /* test for < sizeof-1 since a glibc 2.0.x return of sizeof-1 might
269 ASSERT (explen
>= 0 && explen
< sizeof(exponent
)-1);
271 sprintf (exponent
, p
->expfmt
, expsign
, expval
);
272 explen
= strlen (exponent
);
273 ASSERT (explen
< sizeof(exponent
));
275 TRACE (printf (" expfmt %s gives %s\n", p
->expfmt
, exponent
));
281 /*FALLTHRU*/ /* to stop variables looking uninitialized */
283 case DOPRNT_CONV_GENERAL
:
284 /* The exponent for "scientific" will be exp-1, choose scientific if
285 this is < -4 or >= prec (and minimum 1 for prec). For f==0 will have
286 exp==0 and get the desired "fixed". This rule follows glibc. For
287 fixed there's no need to truncate, the desired ndigits will already
289 if (exp
-1 < -4 || exp
-1 >= MAX (1, prec
))
295 TRACE (printf (" intlen %d intzeros %d fraczeros %d fraclen %d\n",
296 intlen
, intzeros
, fraczeros
, fraclen
));
297 ASSERT (p
->prec
<= -1
298 ? intlen
+ fraclen
== strlen (s
)
299 : intlen
+ fraclen
<= strlen (s
));
303 /* Pad to requested precision with trailing zeros, for general this is
304 all digits, for fixed and scientific just the fraction. */
305 preczeros
= prec
- (fraczeros
+ fraclen
306 + (p
->conv
== DOPRNT_CONV_GENERAL
307 ? intlen
+ intzeros
: 0));
308 preczeros
= MAX (0, preczeros
);
312 TRACE (printf (" prec=%d showtrailing=%d, pad with preczeros %d\n",
313 prec
, p
->showtrailing
, preczeros
));
315 /* radix point if needed, or if forced */
316 pointlen
= ((fraczeros
+ fraclen
+ preczeros
) != 0 || p
->showpoint
!= 0)
317 ? strlen (point
) : 0;
318 TRACE (printf (" point |%s| pointlen %d\n", point
, pointlen
));
320 /* Notice the test for a non-zero value is done after any truncation for
321 DOPRNT_CONV_FIXED. */
324 switch (p
->showbase
) {
328 case DOPRNT_SHOWBASE_NO
:
330 case DOPRNT_SHOWBASE_NONZERO
:
331 if (intlen
== 0 && fraclen
== 0)
334 case DOPRNT_SHOWBASE_YES
:
336 case 16: showbase
= "0x"; showbaselen
= 2; break;
337 case -16: showbase
= "0X"; showbaselen
= 2; break;
338 case 8: showbase
= "0"; showbaselen
= 1; break;
342 TRACE (printf (" showbase %s showbaselen %d\n",
343 showbase
== NULL
? "" : showbase
, showbaselen
));
345 /* left over field width */
346 justlen
= p
->width
- (signlen
+ showbaselen
+ intlen
+ intzeros
+ pointlen
347 + fraczeros
+ fraclen
+ preczeros
+ explen
);
348 TRACE (printf (" justlen %d fill 0x%X\n", justlen
, p
->fill
));
350 justify
= p
->justify
;
351 if (justlen
<= 0) /* no justifying if exceed width */
352 justify
= DOPRNT_JUSTIFY_NONE
;
354 TRACE (printf (" justify type %d intlen %d pointlen %d fraclen %d\n",
355 justify
, intlen
, pointlen
, fraclen
));
357 if (justify
== DOPRNT_JUSTIFY_RIGHT
) /* pad for right */
358 DOPRNT_REPS (p
->fill
, justlen
);
360 if (signlen
) /* sign */
361 DOPRNT_REPS (sign
, 1);
363 DOPRNT_MEMORY_MAYBE (showbase
, showbaselen
); /* base */
365 if (justify
== DOPRNT_JUSTIFY_INTERNAL
) /* pad for internal */
366 DOPRNT_REPS (p
->fill
, justlen
);
368 DOPRNT_MEMORY (s
, intlen
); /* integer */
369 DOPRNT_REPS_MAYBE ('0', intzeros
);
371 DOPRNT_MEMORY_MAYBE (point
, pointlen
); /* point */
373 DOPRNT_REPS_MAYBE ('0', fraczeros
); /* frac */
374 DOPRNT_MEMORY_MAYBE (s
+intlen
, fraclen
);
376 DOPRNT_REPS_MAYBE ('0', preczeros
); /* prec */
378 DOPRNT_MEMORY_MAYBE (exponent
, explen
); /* exp */
380 if (justify
== DOPRNT_JUSTIFY_LEFT
) /* pad for left */
381 DOPRNT_REPS (p
->fill
, justlen
);
384 (*__gmp_free_func
) (free_ptr
, free_size
);