1 /* tprintf.c -- test file for mpfr_printf and mpfr_vprintf
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 only the return value is tested (output string length).
25 Knowing the implementation, we may need only some minimal checks:
26 all the formatted output functions are based on mpfr_vasnprintf_aux
27 and full checks are done via tsprintf. */
28
29 /* Needed due to the tests on HAVE_STDARG and MPFR_USE_MINI_GMP */
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #if defined(HAVE_STDARG) && !defined(MPFR_USE_MINI_GMP)
35 #include <stdarg.h>
36
37 #include <stddef.h>
38 #include <errno.h>
39
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43
44 #define MPFR_NEED_INTMAX_H
45 #include "mpfr-test.h"
46 #define STDOUT_FILENO 1
47
48 #define QUOTE(X) NAME(X)
49 #define NAME(X) #X
50
51 /* unlike other tests, we print out errors to stderr because stdout might be
52 redirected */
53 #define check_length(num_test, var, value, var_spec) \
54 if ((var) != (value)) \
55 { \
56 fprintf (stderr, "Error in test #%d: mpfr_printf printed %" \
57 QUOTE(var_spec)" characters instead of %d\n", \
58 (num_test), (var), (value)); \
59 exit (1); \
60 }
61
62 #define check_length_with_cmp(num_test, var, value, cmp, var_spec) \
63 if (cmp != 0) \
64 { \
65 mpfr_fprintf (stderr, "Error in test #%d: mpfr_printf printed %" \
66 QUOTE(var_spec)" characters instead of %d\n", \
67 (num_test), (var), (value)); \
68 exit (1); \
69 }
70
71 #if MPFR_LCONV_DPTS
72 #define DPLEN ((int) strlen (localeconv()->decimal_point))
73 #else
74 #define DPLEN 1
75 #endif
76
77 /* limit for random precision in random() */
78 const int prec_max_printf = 5000;
79 /* boolean: is stdout redirected to a file ? */
80 int stdout_redirect;
81
82 static void
check(const char * fmt,mpfr_ptr x)83 check (const char *fmt, mpfr_ptr x)
84 {
85 if (mpfr_printf (fmt, x) == -1)
86 {
87 fprintf (stderr, "Error 1 in mpfr_printf(\"%s\", ...)\n", fmt);
88
89 exit (1);
90 }
91 putchar ('\n');
92 }
93
94 static void
check_vprintf(const char * fmt,...)95 check_vprintf (const char *fmt, ...)
96 {
97 va_list ap;
98
99 va_start (ap, fmt);
100 if (mpfr_vprintf (fmt, ap) == -1)
101 {
102 fprintf (stderr, "Error 2 in mpfr_vprintf(\"%s\", ...)\n", fmt);
103
104 va_end (ap);
105 exit (1);
106 }
107 putchar ('\n');
108 va_end (ap);
109 }
110
111 static unsigned int
check_vprintf_failure(const char * fmt,...)112 check_vprintf_failure (const char *fmt, ...)
113 {
114 va_list ap;
115 int r, e;
116
117 va_start (ap, fmt);
118 errno = 0;
119 r = mpfr_vprintf (fmt, ap);
120 e = errno;
121 va_end (ap);
122
123 if (r != -1
124 #ifdef EOVERFLOW
125 || e != EOVERFLOW
126 #endif
127 )
128 {
129 putchar ('\n');
130 fprintf (stderr, "Error 3 in mpfr_vprintf(\"%s\", ...)\n"
131 "Got r = %d, errno = %d\n", fmt, r, e);
132 return 1;
133 }
134
135 putchar ('\n');
136 return 0;
137 }
138
139 /* The goal of this test is to check cases where more INT_MAX characters
140 are output, in which case, it should be a failure, because like C's
141 *printf functions, the return type is int and the returned value must
142 be either the number of characters printed or a negative value. */
143 static void
check_long_string(void)144 check_long_string (void)
145 {
146 /* this test is VERY expensive both in time (~1 min on core2 @ 2.40GHz) and
147 in memory (~2.5 GB) */
148 mpfr_t x;
149 long large_prec = 2147483647;
150 size_t min_memory_limit, old_memory_limit;
151
152 old_memory_limit = tests_memory_limit;
153
154 /* With a 32-bit (4GB) address space, a realloc failure has been noticed
155 with a 2G precision (though allocating up to 4GB is possible):
156 MPFR: Can't reallocate memory (old_size=4096 new_size=2147487744)
157 The implementation might be improved to use less memory and avoid
158 this problem. In the mean time, let's choose a smaller precision,
159 but this will generally have the effect to disable the test. */
160 if (sizeof (void *) == 4)
161 large_prec /= 2;
162
163 /* We assume that the precision won't be increased internally. */
164 if (large_prec > MPFR_PREC_MAX)
165 large_prec = MPFR_PREC_MAX;
166
167 /* Increase tests_memory_limit if need be in order to avoid an
168 obvious failure due to insufficient memory. Note that such an
169 increase is necessary, but is not guaranteed to be sufficient
170 in all cases (e.g. with logging activated). */
171 min_memory_limit = large_prec / MPFR_BYTES_PER_MP_LIMB;
172 if (min_memory_limit > (size_t) -1 / 32)
173 min_memory_limit = (size_t) -1;
174 else
175 min_memory_limit *= 32;
176 if (tests_memory_limit > 0 && tests_memory_limit < min_memory_limit)
177 tests_memory_limit = min_memory_limit;
178
179 mpfr_init2 (x, large_prec);
180
181 mpfr_set_ui (x, 1, MPFR_RNDN);
182 mpfr_nextabove (x);
183
184 if (large_prec >= INT_MAX - 512)
185 {
186 unsigned int err = 0;
187
188 #define LS1 "%Rb %512d"
189 #define LS2 "%RA %RA %Ra %Ra %512d"
190
191 err |= check_vprintf_failure (LS1, x, 1);
192 err |= check_vprintf_failure (LS2, x, x, x, x, 1);
193
194 if (sizeof (long) * CHAR_BIT > 40)
195 {
196 long n1, n2;
197
198 n1 = large_prec + 517;
199 n2 = -17;
200 err |= check_vprintf_failure (LS1 "%ln", x, 1, &n2);
201 if (n1 != n2)
202 {
203 fprintf (stderr, "Error in check_long_string(\"%s\", ...)\n"
204 "Expected n = %ld\n"
205 "Got n = %ld\n",
206 LS1 "%ln", n1, n2);
207 err = 1;
208 }
209 n1 = ((large_prec - 2) / 4) * 4 + 548;
210 n2 = -17;
211 err |= check_vprintf_failure (LS2 "%ln", x, x, x, x, 1, &n2);
212 if (n1 != n2)
213 {
214 fprintf (stderr, "Error in check_long_string(\"%s\", ...)\n"
215 "Expected n = %ld\n"
216 "Got n = %ld\n",
217 LS2 "%ln", n1, n2);
218 err = 1;
219 }
220 }
221
222 if (err)
223 exit (1);
224 }
225
226 mpfr_clear (x);
227 tests_memory_limit = old_memory_limit;
228 }
229
230 static void
check_special(void)231 check_special (void)
232 {
233 mpfr_t x;
234
235 mpfr_init (x);
236
237 mpfr_set_inf (x, 1);
238 check ("%Ra", x);
239 check ("%Rb", x);
240 check ("%Re", x);
241 check ("%Rf", x);
242 check ("%Rg", x);
243 check_vprintf ("%Ra", x);
244 check_vprintf ("%Rb", x);
245 check_vprintf ("%Re", x);
246 check_vprintf ("%Rf", x);
247 check_vprintf ("%Rg", x);
248
249 mpfr_set_inf (x, -1);
250 check ("%Ra", x);
251 check ("%Rb", x);
252 check ("%Re", x);
253 check ("%Rf", x);
254 check ("%Rg", x);
255 check_vprintf ("%Ra", x);
256 check_vprintf ("%Rb", x);
257 check_vprintf ("%Re", x);
258 check_vprintf ("%Rf", x);
259 check_vprintf ("%Rg", x);
260
261 mpfr_set_nan (x);
262 check ("%Ra", x);
263 check ("%Rb", x);
264 check ("%Re", x);
265 check ("%Rf", x);
266 check ("%Rg", x);
267 check_vprintf ("%Ra", x);
268 check_vprintf ("%Rb", x);
269 check_vprintf ("%Re", x);
270 check_vprintf ("%Rf", x);
271 check_vprintf ("%Rg", x);
272
273 mpfr_clear (x);
274 }
275
276 static void
check_mixed(void)277 check_mixed (void)
278 {
279 int ch = 'a';
280 #ifndef NPRINTF_HH
281 signed char sch = -1;
282 unsigned char uch = 1;
283 #endif
284 short sh = -1;
285 unsigned short ush = 1;
286 int i = -1;
287 int j = 1;
288 unsigned int ui = 1;
289 long lo = -1;
290 unsigned long ulo = 1;
291 float f = -1.25;
292 double d = -1.25;
293 #if defined(PRINTF_T) || defined(PRINTF_L)
294 long double ld = -1.25;
295 #endif
296
297 #ifdef PRINTF_T
298 ptrdiff_t p = 1, saved_p;
299 #endif
300 size_t sz = 1;
301
302 mpz_t mpz;
303 mpq_t mpq;
304 mpf_t mpf;
305 mpfr_rnd_t rnd = MPFR_RNDN;
306
307 mpfr_t mpfr;
308 mpfr_prec_t prec;
309
310 mpz_init (mpz);
311 mpz_set_ui (mpz, ulo);
312 mpq_init (mpq);
313 mpq_set_si (mpq, lo, ulo);
314 mpf_init (mpf);
315 mpf_set_q (mpf, mpq);
316 mpfr_init (mpfr);
317 mpfr_set_f (mpfr, mpf, MPFR_RNDN);
318 prec = mpfr_get_prec (mpfr);
319
320 check_vprintf ("a. %Ra, b. %u, c. %lx%n", mpfr, ui, ulo, &j);
321 check_length (1, j, 22, d);
322 check_vprintf ("a. %c, b. %Rb, c. %u, d. %li%ln", i, mpfr, i, lo, &ulo);
323 check_length (2, ulo, 36, lu);
324 check_vprintf ("a. %hi, b. %*f, c. %Re%hn", ush, 3, f, mpfr, &ush);
325 check_length (3, ush, 45 + DPLEN, hu);
326 check_vprintf ("a. %hi, b. %f, c. %#.2Rf%n", sh, d, mpfr, &i);
327 check_length (4, i, 28 + DPLEN, d);
328 check_vprintf ("a. %R*A, b. %Fe, c. %i%zn", rnd, mpfr, mpf, sz, &sz);
329 check_length (5, (unsigned long) sz, 33 + DPLEN, lu); /* no format specifier '%zu' in C90 */
330 check_vprintf ("a. %Pu, b. %c, c. %RUG, d. %Zi%Zn", prec, ch, mpfr, mpz, &mpz);
331 check_length_with_cmp (6, mpz, 24, mpz_cmp_ui (mpz, 24), Zi);
332 check_vprintf ("%% a. %#.0RNg, b. %Qx%Rn c. %p",
333 mpfr, mpq, &mpfr, (void *) &i);
334 check_length_with_cmp (7, mpfr, 15, mpfr_cmp_ui (mpfr, 15), Rg);
335
336 #ifdef PRINTF_T
337 saved_p = p;
338 check_vprintf ("%% a. %RNg, b. %Qx, c. %td%tn", mpfr, mpq, p, &p);
339 if (p != 20)
340 {
341 mpfr_fprintf (stderr,
342 "Error in test #8: got '%% a. %RNg, b. %Qx, c. %td'\n",
343 mpfr, mpq, saved_p);
344 #if defined(__MINGW32__) || defined(__MINGW64__)
345 fprintf (stderr,
346 "Your MinGW may be too old, in which case compiling GMP\n"
347 "with -D__USE_MINGW_ANSI_STDIO might be required.\n");
348 #endif
349 }
350 check_length (8, (long) p, 20, ld); /* no format specifier '%td' in C90 */
351 #endif
352
353 #ifdef PRINTF_L
354 check_vprintf ("a. %RA, b. %Lf, c. %QX%zn", mpfr, ld, mpq, &sz);
355 check_length (9, (unsigned long) sz, 29 + DPLEN, lu); /* no format specifier '%zu' in C90 */
356 #endif
357
358 #ifndef NPRINTF_HH
359 check_vprintf ("a. %hhi, b. %Ra, c. %hhu%hhn", sch, mpfr, uch, &uch);
360 check_length (10, (unsigned int) uch, 22, u); /* no format specifier '%hhu' in C90 */
361 #endif
362
363 #if defined(HAVE_LONG_LONG) && !defined(NPRINTF_LL)
364 {
365 long long llo = -1;
366 unsigned long long ullo = 1;
367
368 check_vprintf ("a. %Re, b. %llx%Qn", mpfr, ullo, &mpq);
369 check_length_with_cmp (11, mpq, 31, mpq_cmp_ui (mpq, 31, 1), Qu);
370 check_vprintf ("a. %lli, b. %Rf%lln", llo, mpfr, &ullo);
371 check_length (12, ullo, 19, llu);
372 }
373 #endif
374
375 #if defined(_MPFR_H_HAVE_INTMAX_T) && !defined(NPRINTF_J)
376 {
377 intmax_t im = -1;
378 uintmax_t uim = 1;
379
380 check_vprintf ("a. %*RA, b. %ji%Fn", 10, mpfr, im, &mpf);
381 check_length_with_cmp (31, mpf, 20, mpf_cmp_ui (mpf, 20), Fg);
382 check_vprintf ("a. %.*Re, b. %jx%jn", 10, mpfr, uim, &im);
383 check_length (32, (long) im, 25, li); /* no format specifier "%ji" in C90 */
384 }
385 #endif
386
387 mpfr_clear (mpfr);
388 mpf_clear (mpf);
389 mpq_clear (mpq);
390 mpz_clear (mpz);
391 }
392
393 static void
check_random(int nb_tests)394 check_random (int nb_tests)
395 {
396 int i;
397 mpfr_t x;
398 mpfr_rnd_t rnd;
399 char flag[] =
400 {
401 '-',
402 '+',
403 ' ',
404 '#',
405 '0', /* no ambiguity: first zeros are flag zero*/
406 '\''
407 };
408 char specifier[] =
409 {
410 'a',
411 'b',
412 'e',
413 'f',
414 'g'
415 };
416 mpfr_exp_t old_emin, old_emax;
417
418 old_emin = mpfr_get_emin ();
419 old_emax = mpfr_get_emax ();
420
421 mpfr_init (x);
422
423 for (i = 0; i < nb_tests; ++i)
424 {
425 int ret;
426 int j, jmax;
427 int spec, prec;
428 #define FMT_SIZE 13
429 char fmt[FMT_SIZE]; /* at most something like "%-+ #0'.*R*f" */
430 char *ptr = fmt;
431
432 tests_default_random (x, 256, MPFR_EMIN_MIN, MPFR_EMAX_MAX, 0);
433 rnd = (mpfr_rnd_t) RND_RAND ();
434
435 spec = (int) (randlimb () % 5);
436 jmax = (spec == 3 || spec == 4) ? 6 : 5; /* ' flag only with %f or %g */
437 /* advantage small precision */
438 prec = RAND_BOOL () ? 10 : prec_max_printf;
439 prec = (int) (randlimb () % prec);
440 if (spec == 3
441 && (mpfr_get_exp (x) > prec_max_printf
442 || mpfr_get_exp (x) < -prec_max_printf))
443 /* change style 'f' to style 'e' when number x is very large or very
444 small*/
445 --spec;
446
447 *ptr++ = '%';
448 for (j = 0; j < jmax; j++)
449 {
450 if (randlimb () % 3 == 0)
451 *ptr++ = flag[j];
452 }
453 *ptr++ = '.';
454 *ptr++ = '*';
455 *ptr++ = 'R';
456 *ptr++ = '*';
457 *ptr++ = specifier[spec];
458 *ptr = '\0';
459 MPFR_ASSERTD (ptr - fmt < FMT_SIZE);
460
461 mpfr_printf ("mpfr_printf(\"%s\", %d, %s, %Re)\n", fmt, prec,
462 mpfr_print_rnd_mode (rnd), x);
463 ret = mpfr_printf (fmt, prec, rnd, x);
464 if (ret == -1)
465 {
466 if (spec == 3
467 && (MPFR_GET_EXP (x) > INT_MAX || MPFR_GET_EXP (x) < -INT_MAX))
468 /* normal failure: x is too large to be output with full precision */
469 {
470 mpfr_printf ("too large !");
471 }
472 else
473 {
474 printf ("Error in mpfr_printf(\"%s\", %d, %s, ...)",
475 fmt, prec, mpfr_print_rnd_mode (rnd));
476
477 if (stdout_redirect)
478 {
479 if ((fflush (stdout) == EOF) || (fclose (stdout) == EOF))
480 {
481 perror ("check_random");
482 exit (1);
483 }
484 }
485 exit (1);
486 }
487 }
488 putchar ('\n');
489 }
490
491 set_emin (old_emin);
492 set_emax (old_emax);
493
494 mpfr_clear (x);
495 }
496
497 #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
498
499 static void
test_locale(void)500 test_locale (void)
501 {
502 const char * const tab_locale[] = {
503 "en_US",
504 "en_US.iso88591",
505 "en_US.iso885915",
506 "en_US.utf8"
507 };
508 int i;
509 mpfr_t x;
510 int count;
511 char v[] = "99999999999999999999999.5";
512
513 for (i = 0; i < numberof(tab_locale); i++)
514 {
515 char *s;
516
517 s = setlocale (LC_ALL, tab_locale[i]);
518
519 if (s != NULL && MPFR_THOUSANDS_SEPARATOR == ',')
520 break;
521 }
522
523 if (i == numberof(tab_locale))
524 {
525 if (getenv ("MPFR_CHECK_LOCALES") == NULL)
526 return;
527
528 fprintf (stderr, "Cannot find a locale with ',' thousands separator.\n"
529 "Please install one of the en_US based locales.\n");
530 exit (1);
531 }
532
533 mpfr_init2 (x, 113);
534 mpfr_set_ui (x, 10000, MPFR_RNDN);
535
536 count = mpfr_printf ("(1) 10000=%'Rg \n", x);
537 check_length (10000, count, 18, d);
538 count = mpfr_printf ("(2) 10000=%'Rf \n", x);
539 check_length (10001, count, 25, d);
540
541 mpfr_set_ui (x, 1000, MPFR_RNDN);
542 count = mpfr_printf ("(3) 1000=%'Rf \n", x);
543 check_length (10002, count, 23, d);
544
545 for (i = 1; i <= sizeof (v) - 3; i++)
546 {
547 mpfr_set_str (x, v + sizeof (v) - 3 - i, 10, MPFR_RNDN);
548 count = mpfr_printf ("(4) 10^i=%'.0Rf \n", x);
549 check_length (10002 + i, count, 12 + i + i/3, d);
550 }
551
552 #define N0 20
553
554 for (i = 1; i <= N0; i++)
555 {
556 char s[N0+4];
557 int j, rnd;
558
559 s[0] = '1';
560 for (j = 1; j <= i; j++)
561 s[j] = '0';
562 s[i+1] = '\0';
563
564 mpfr_set_str (x, s, 10, MPFR_RNDN);
565
566 RND_LOOP (rnd)
567 {
568 count = mpfr_printf ("(5) 10^i=%'.0R*f \n", (mpfr_rnd_t) rnd, x);
569 check_length (11000 + 10 * i + rnd, count, 12 + i + i/3, d);
570 }
571
572 strcat (s + (i + 1), ".5");
573 count = mpfr_printf ("(5) 10^i=%'.0Rf \n", x);
574 check_length (11000 + 10 * i + 9, count, 12 + i + i/3, d);
575 }
576
577 mpfr_set_str (x, "1000", 10, MPFR_RNDN);
578 count = mpfr_printf ("%'012.3Rg\n", x);
579 check_length (12000, count, 13, d);
580 count = mpfr_printf ("%'012.4Rg\n", x);
581 check_length (12001, count, 13, d);
582 count = mpfr_printf ("%'013.4Rg\n", x);
583 check_length (12002, count, 14, d);
584
585 mpfr_clear (x);
586 }
587
588 #else
589
590 static void
test_locale(void)591 test_locale (void)
592 {
593 if (getenv ("MPFR_CHECK_LOCALES") != NULL)
594 {
595 fprintf (stderr, "Cannot test locales.\n");
596 exit (1);
597 }
598 }
599
600 #endif
601
602 int
main(int argc,char * argv[])603 main (int argc, char *argv[])
604 {
605 int N;
606
607 tests_start_mpfr ();
608
609 /* with no argument: prints to /dev/null,
610 tprintf N: prints N tests to stdout */
611 if (argc == 1)
612 {
613 N = 1000;
614 stdout_redirect = 1;
615 if (freopen ("/dev/null", "w", stdout) == NULL)
616 {
617 /* We failed to open this device, try with a dummy file */
618 if (freopen ("tprintf_out.txt", "w", stdout) == NULL)
619 {
620 /* Output the error message to stderr since it is not
621 a message about a wrong result in MPFR. Anyway the
622 standard output may have changed. */
623 fprintf (stderr, "Can't open /dev/null or a temporary file\n");
624 exit (1);
625 }
626 }
627 }
628 else
629 {
630 stdout_redirect = 0;
631 N = atoi (argv[1]);
632 }
633
634 check_special ();
635 check_mixed ();
636
637 /* expensive tests */
638 if (getenv ("MPFR_CHECK_LARGEMEM") != NULL)
639 check_long_string();
640
641 check_random (N);
642
643 test_locale ();
644
645 if (stdout_redirect)
646 {
647 if ((fflush (stdout) == EOF) || (fclose (stdout) == EOF))
648 perror ("main");
649 }
650 tests_end_mpfr ();
651 return 0;
652 }
653
654 #else /* HAVE_STDARG */
655
656 int
main(void)657 main (void)
658 {
659 /* We have nothing to test. */
660 return 77;
661 }
662
663 #endif /* HAVE_STDARG */
664