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