1 /* tfprintf.c -- test file for mpfr_fprintf and mpfr_vfprintf
2
3 Copyright 2008-2023 Free Software Foundation, Inc.
4 Contributed by the AriC and Caramba projects, INRIA.
5
6 This file is part of the GNU MPFR Library.
7
8 The GNU MPFR Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12
13 The GNU MPFR Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see
20 https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
21 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22
23 /* FIXME: The output is not tested (thus coverage data are meaningless).
24 For instance, slightly changing the code of mpfr_fprintf does not
25 trigger any failure in the test suite.
26 Knowing the implementation, we may need only some minimal checks:
27 all the formatted output functions are based on mpfr_vasnprintf_aux
28 and full checks are done via tsprintf. */
29
30 /* Needed due to the tests on HAVE_STDARG and MPFR_USE_MINI_GMP */
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #if defined(HAVE_STDARG) && !defined(MPFR_USE_MINI_GMP)
36 #include <stdarg.h>
37
38 #include <float.h>
39 #include <stddef.h>
40
41 #define MPFR_NEED_INTMAX_H
42 #include "mpfr-test.h"
43
44 #define QUOTE(X) NAME(X)
45 #define NAME(X) #X
46
47 #define check_length(num_test, var, value, var_spec) \
48 if ((var) != (value)) \
49 { \
50 printf ("Error in test #%d: mpfr_vfprintf printed %" QUOTE(var_spec) \
51 " characters instead of %d\n", (num_test), (var), (value)); \
52 exit (1); \
53 }
54
55 #define check_length_with_cmp(num_test, var, value, cmp, var_spec) \
56 if (cmp != 0) \
57 { \
58 mpfr_printf ("Error in test #%d: mpfr_vfprintf printed %" \
59 QUOTE(var_spec) " characters instead of %d\n", \
60 (num_test), (var), (value)); \
61 exit (1); \
62 }
63
64 #if MPFR_LCONV_DPTS
65 #define DPLEN ((int) strlen (localeconv()->decimal_point))
66 #else
67 #define DPLEN 1
68 #endif
69
70 /* limit for random precision in random() */
71 const int prec_max_printf = 5000;
72
73 static void
check(FILE * fout,const char * fmt,mpfr_ptr x)74 check (FILE *fout, const char *fmt, mpfr_ptr x)
75 {
76 if (mpfr_fprintf (fout, fmt, x) == -1)
77 {
78 mpfr_printf ("Error in mpfr_fprintf(fout, \"%s\", %Re)\n",
79 fmt, x);
80 exit (1);
81 }
82 fputc ('\n', fout);
83 }
84
85 static void
check_vfprintf(FILE * fout,const char * fmt,...)86 check_vfprintf (FILE *fout, const char *fmt, ...)
87 {
88 va_list ap;
89
90 va_start (ap, fmt);
91 if (mpfr_vfprintf (fout, fmt, ap) == -1)
92 {
93 mpfr_printf ("Error in mpfr_vfprintf(fout, \"%s\", ...)\n", fmt);
94
95 va_end (ap);
96 exit (1);
97 }
98
99 va_end (ap);
100 fputc ('\n', fout);
101 }
102
103 static void
check_special(FILE * fout)104 check_special (FILE *fout)
105 {
106 mpfr_t x;
107
108 mpfr_init (x);
109
110 mpfr_set_inf (x, 1);
111 check (fout, "%Ra", x);
112 check (fout, "%Rb", x);
113 check (fout, "%Re", x);
114 check (fout, "%Rf", x);
115 check (fout, "%Rg", x);
116 check_vfprintf (fout, "%Ra", x);
117 check_vfprintf (fout, "%Rb", x);
118 check_vfprintf (fout, "%Re", x);
119 check_vfprintf (fout, "%Rf", x);
120 check_vfprintf (fout, "%Rg", x);
121
122 mpfr_set_inf (x, -1);
123 check (fout, "%Ra", x);
124 check (fout, "%Rb", x);
125 check (fout, "%Re", x);
126 check (fout, "%Rf", x);
127 check (fout, "%Rg", x);
128 check_vfprintf (fout, "%Ra", x);
129 check_vfprintf (fout, "%Rb", x);
130 check_vfprintf (fout, "%Re", x);
131 check_vfprintf (fout, "%Rf", x);
132 check_vfprintf (fout, "%Rg", x);
133
134 mpfr_set_nan (x);
135 check (fout, "%Ra", x);
136 check (fout, "%Rb", x);
137 check (fout, "%Re", x);
138 check (fout, "%Rf", x);
139 check (fout, "%Rg", x);
140 check_vfprintf (fout, "%Ra", x);
141 check_vfprintf (fout, "%Rb", x);
142 check_vfprintf (fout, "%Re", x);
143 check_vfprintf (fout, "%Rf", x);
144 check_vfprintf (fout, "%Rg", x);
145
146 mpfr_clear (x);
147 }
148
149 static void
check_mixed(FILE * fout)150 check_mixed (FILE *fout)
151 {
152 int ch = 'a';
153 #ifndef NPRINTF_HH
154 signed char sch = -1;
155 unsigned char uch = 1;
156 #endif
157 short sh = -1;
158 unsigned short ush = 1;
159 int i = -1;
160 int j = 1;
161 unsigned int ui = 1;
162 long lo = -1;
163 unsigned long ulo = 1;
164 float f = -1.25;
165 double d = -1.25;
166 #if defined(PRINTF_T) || defined(PRINTF_L)
167 long double ld = -1.25;
168 #endif
169
170 #ifdef PRINTF_T
171 ptrdiff_t p = 1, saved_p;
172 #endif
173 size_t sz = 1;
174
175 mpz_t mpz;
176 mpq_t mpq;
177 mpf_t mpf;
178 mpfr_rnd_t rnd = MPFR_RNDN;
179
180 mp_size_t limb_size = 3;
181 mp_limb_t limb[3];
182
183 mpfr_t mpfr;
184 mpfr_prec_t prec = 53;
185
186 mpz_init (mpz);
187 mpz_set_ui (mpz, ulo);
188 mpq_init (mpq);
189 mpq_set_si (mpq, lo, ulo);
190 mpf_init (mpf);
191 mpf_set_q (mpf, mpq);
192
193 mpfr_init2 (mpfr, prec);
194 mpfr_set_f (mpfr, mpf, MPFR_RNDN);
195
196 limb[0] = limb[1] = limb[2] = MPFR_LIMB_MAX;
197
198 check_vfprintf (fout, "a. %Ra, b. %u, c. %lx%n", mpfr, ui, ulo, &j);
199 check_length (1, j, 22, d);
200 check_vfprintf (fout, "a. %c, b. %Rb, c. %u, d. %li%ln", i, mpfr, i,
201 lo, &ulo);
202 check_length (2, ulo, 36, lu);
203 check_vfprintf (fout, "a. %hi, b. %*f, c. %Re%hn", ush, 3, f, mpfr, &ush);
204 check_length (3, ush, 45 + DPLEN, hu);
205 check_vfprintf (fout, "a. %hi, b. %f, c. %#.2Rf%n", sh, d, mpfr, &i);
206 check_length (4, i, 28 + DPLEN, d);
207 check_vfprintf (fout, "a. %R*A, b. %Fe, c. %i%zn", rnd, mpfr, mpf, sz,
208 &sz);
209 check_length (5, (unsigned long) sz, 33 + DPLEN, lu); /* no format specifier "%zu" in C90 */
210 check_vfprintf (fout, "a. %Pu, b. %c, c. %Zi%Zn", prec, ch, mpz, &mpz);
211 check_length_with_cmp (6, mpz, 17, mpz_cmp_ui (mpz, 17), Zi);
212 check_vfprintf (fout, "%% a. %#.0RNg, b. %Qx%Rn, c. %p", mpfr, mpq, &mpfr,
213 (void *) &i);
214 check_length_with_cmp (7, mpfr, 15, mpfr_cmp_ui (mpfr, 15), Rg);
215
216 #ifdef PRINTF_T
217 saved_p = p;
218 check_vfprintf (fout, "%% a. %RNg, b. %Qx, c. %td%tn", mpfr, mpq, p, &p);
219 if (p != 20)
220 {
221 mpfr_fprintf (stderr,
222 "Error in test #8: got '%% a. %RNg, b. %Qx, c. %td'\n",
223 mpfr, mpq, saved_p);
224 #if defined(__MINGW32__) || defined(__MINGW64__)
225 fprintf (stderr,
226 "Your MinGW may be too old, in which case compiling GMP\n"
227 "with -D__USE_MINGW_ANSI_STDIO might be required.\n");
228 #endif
229 }
230 check_length (8, (long) p, 20, ld); /* no format specifier "%td" in C90 */
231 #endif
232
233 #ifdef PRINTF_L
234 check_vfprintf (fout, "a. %RA, b. %Lf, c. %QX%zn", mpfr, ld, mpq, &sz);
235 check_length (9, (unsigned long) sz, 29 + DPLEN, lu); /* no format specifier "%zu" in C90 */
236 #endif
237
238 #ifndef NPRINTF_HH
239 check_vfprintf (fout, "a. %hhi, b. %RA, c. %hhu%hhn", sch, mpfr, uch, &uch);
240 check_length (10, (unsigned int) uch, 22, u); /* no format specifier "%hhu" in C90 */
241 #endif
242
243 #if (__GNU_MP_VERSION * 10 + __GNU_MP_VERSION_MINOR) >= 42
244 /* The 'M' specifier was added in gmp 4.2.0 */
245 check_vfprintf (fout, "a. %Mx b. %Re%Mn", limb[0], mpfr, &limb[0]);
246 if (limb[0] != 29 + GMP_NUMB_BITS / 4 ||
247 limb[1] != MPFR_LIMB_MAX ||
248 limb[2] != MPFR_LIMB_MAX)
249 {
250 printf ("Error in test #11: mpfr_vfprintf did not print %d characters"
251 " as expected\n", 29 + (int) GMP_NUMB_BITS / 4);
252 exit (1);
253 }
254
255 limb[0] = MPFR_LIMB_MAX;
256 /* we tell vfprintf that limb array is 2 cells wide
257 and check it doesn't go through */
258 check_vfprintf (fout, "a. %Re .b %Nx%Nn", mpfr, limb, limb_size, limb,
259 limb_size - 1);
260 if (limb[0] != 29 + 3 * GMP_NUMB_BITS / 4 ||
261 limb[1] != MPFR_LIMB_ZERO ||
262 limb[2] != MPFR_LIMB_MAX)
263 {
264 printf ("Error in test #12: mpfr_vfprintf did not print %d characters"
265 " as expected\n", 29 + (int) GMP_NUMB_BITS / 4);
266 exit (1);
267 }
268 #endif
269
270 #if defined(HAVE_LONG_LONG) && !defined(NPRINTF_LL)
271 {
272 long long llo = -1;
273 unsigned long long ullo = 1;
274
275 check_vfprintf (fout, "a. %Re, b. %llx%Qn", mpfr, ullo, &mpq);
276 check_length_with_cmp (21, mpq, 31, mpq_cmp_ui (mpq, 31, 1), Qu);
277 check_vfprintf (fout, "a. %lli, b. %Rf%Fn", llo, mpfr, &mpf);
278 check_length_with_cmp (22, mpf, 19, mpf_cmp_ui (mpf, 19), Fg);
279 }
280 #endif
281
282 #if defined(_MPFR_H_HAVE_INTMAX_T) && !defined(NPRINTF_J)
283 {
284 intmax_t im = -1;
285 uintmax_t uim = 1;
286
287 check_vfprintf (fout, "a. %*RA, b. %ji%Qn", 10, mpfr, im, &mpq);
288 check_length_with_cmp (31, mpq, 20, mpq_cmp_ui (mpq, 20, 1), Qu);
289 check_vfprintf (fout, "a. %.*Re, b. %jx%Fn", 10, mpfr, uim, &mpf);
290 check_length_with_cmp (32, mpf, 25, mpf_cmp_ui (mpf, 25), Fg);
291 }
292 #endif
293
294 mpfr_clear (mpfr);
295 mpf_clear (mpf);
296 mpq_clear (mpq);
297 mpz_clear (mpz);
298 }
299
300 static void
check_random(FILE * fout,int nb_tests)301 check_random (FILE *fout, int nb_tests)
302 {
303 int i;
304 mpfr_t x;
305 mpfr_rnd_t rnd;
306 char flag[] =
307 {
308 '-',
309 '+',
310 ' ',
311 '#',
312 '0', /* no ambiguity: first zeros are flag zero*/
313 '\''
314 };
315 char specifier[] =
316 {
317 'a',
318 'b',
319 'e',
320 'f',
321 'g'
322 };
323 mpfr_exp_t old_emin, old_emax;
324
325 old_emin = mpfr_get_emin ();
326 old_emax = mpfr_get_emax ();
327
328 mpfr_init (x);
329
330 for (i = 0; i < nb_tests; ++i)
331 {
332 int ret;
333 int j, jmax;
334 int spec, prec;
335 #define FMT_SIZE 13
336 char fmt[FMT_SIZE]; /* at most something like "%-+ #0'.*R*f" */
337 char *ptr = fmt;
338
339 tests_default_random (x, 256, MPFR_EMIN_MIN, MPFR_EMAX_MAX, 0);
340 rnd = RND_RAND ();
341
342 spec = (int) (randlimb () % 5);
343 jmax = (spec == 3 || spec == 4) ? 6 : 5; /* ' flag only with %f or %g */
344 /* advantage small precision */
345 prec = RAND_BOOL () ? 10 : prec_max_printf;
346 prec = (int) (randlimb () % prec);
347 if (spec == 3
348 && (mpfr_get_exp (x) > prec_max_printf
349 || mpfr_get_exp (x) < -prec_max_printf))
350 /* change style 'f' to style 'e' when number x is large */
351 --spec;
352
353 *ptr++ = '%';
354 for (j = 0; j < jmax; j++)
355 {
356 if (randlimb () % 3 == 0)
357 *ptr++ = flag[j];
358 }
359 *ptr++ = '.';
360 *ptr++ = '*';
361 *ptr++ = 'R';
362 *ptr++ = '*';
363 *ptr++ = specifier[spec];
364 *ptr = '\0';
365 MPFR_ASSERTD (ptr - fmt < FMT_SIZE);
366
367 mpfr_fprintf (fout, "mpfr_fprintf(fout, \"%s\", %d, %s, %Re)\n",
368 fmt, prec, mpfr_print_rnd_mode (rnd), x);
369 ret = mpfr_fprintf (fout, fmt, prec, rnd, x);
370 if (ret == -1)
371 {
372 if (spec == 3
373 && (MPFR_GET_EXP (x) > INT_MAX || MPFR_GET_EXP (x) < -INT_MAX))
374 /* normal failure: x is too large to be output with full precision */
375 {
376 mpfr_fprintf (fout, "too large !");
377 }
378 else
379 {
380 mpfr_printf ("Error in mpfr_fprintf(fout, \"%s\", %d, %s, %Re)\n",
381 fmt, prec, mpfr_print_rnd_mode (rnd), x);
382 exit (1);
383 }
384 }
385 mpfr_fprintf (fout, "\n");
386 }
387
388 set_emin (old_emin);
389 set_emax (old_emax);
390
391 mpfr_clear (x);
392 }
393
394 static void
bug_20090316(FILE * fout)395 bug_20090316 (FILE *fout)
396 {
397 mpfr_t x;
398
399 mpfr_init2 (x, 53);
400
401 /* bug 20090316: fixed in r6112 */
402 mpfr_set_ui_2exp (x, 0x60fa2916, -30, MPFR_RNDN);
403 check (fout, "%-#.4095RDg\n", x);
404
405 mpfr_clear (x);
406 }
407
408 int
main(int argc,char * argv[])409 main (int argc, char *argv[])
410 {
411 FILE *fout;
412 int N;
413
414 tests_start_mpfr ();
415
416 /* with no argument: prints to /dev/null,
417 tfprintf N: prints N tests to stdout */
418 if (argc == 1)
419 {
420 N = 1000;
421 fout = fopen ("/dev/null", "w");
422 /* If we failed to open this device, try with a dummy file */
423 if (fout == NULL)
424 {
425 fout = fopen ("tfprintf_out.txt", "w");
426
427 if (fout == NULL)
428 {
429 printf ("Can't open /dev/null or a temporary file\n");
430 exit (1);
431 }
432 }
433 }
434 else
435 {
436 fout = stdout;
437 N = atoi (argv[1]);
438 }
439
440 check_special (fout);
441 check_mixed (fout);
442 check_random (fout, N);
443
444 bug_20090316 (fout);
445
446 fclose (fout);
447 tests_end_mpfr ();
448 return 0;
449 }
450
451 #else /* HAVE_STDARG */
452
453 int
main(void)454 main (void)
455 {
456 /* We have nothing to test. */
457 return 77;
458 }
459
460 #endif /* HAVE_STDARG */
461