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