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