xref: /netbsd-src/external/lgpl3/mpfr/dist/tests/tprintf.c (revision ec6772edaf0cdcb5f52a48f4aca5e33a8fb8ecfd)
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