xref: /netbsd-src/external/lgpl3/mpfr/dist/tests/tprintf.c (revision e5d758f832e07a177fa24707c434b7ce26d0f762)
1 /* tprintf.c -- test file for mpfr_printf and mpfr_vprintf
2 
3 Copyright 2008-2020 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 /* limit for random precision in random() */
72 const int prec_max_printf = 5000;
73 /* boolean: is stdout redirected to a file ? */
74 int stdout_redirect;
75 
76 static void
77 check (const char *fmt, mpfr_t 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 
88 static void
89 check_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 
105 static unsigned int
106 check_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. */
137 static void
138 check_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 
224 static void
225 check_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 
270 static void
271 check_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 C89 */
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 C89 */
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 C89 */
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 C89 */
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 C89 */
376   }
377 #endif
378 
379   mpfr_clear (mpfr);
380   mpf_clear (mpf);
381   mpq_clear (mpq);
382   mpz_clear (mpz);
383 }
384 
385 static void
386 check_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 = (randlimb () % 2) ? 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) == -1))
472                     {
473                       perror ("check_random");
474                       exit (1);
475                     }
476                 }
477               exit (1);
478             }
479         }
480       putchar ('\n');
481     }
482 
483   mpfr_set_emin (old_emin);
484   mpfr_set_emax (old_emax);
485 
486   mpfr_clear (x);
487 }
488 
489 #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
490 
491 static void
492 test_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 
582 static void
583 test_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 
594 int
595 main (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) == -1))
640         perror ("main");
641     }
642   tests_end_mpfr ();
643   return 0;
644 }
645 
646 #else  /* HAVE_STDARG */
647 
648 int
649 main (void)
650 {
651   /* We have nothing to test. */
652   return 77;
653 }
654 
655 #endif  /* HAVE_STDARG */
656