xref: /netbsd-src/external/lgpl3/mpfr/dist/tests/tfpif.c (revision 2718af68c3efc72c9769069b5c7f9ed36f6b9def)
1 /* Test file for mpfr_fpif.
2 
3 Copyright 2012-2020 Free Software Foundation, Inc.
4 Contributed by Olivier Demengeon.
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 #include <errno.h>
24 
25 #include "mpfr-test.h"
26 
27 #define FILE_NAME_RW "tfpif_rw.dat" /* temporary name (written then read) */
28 #define FILE_NAME_R  "tfpif_r1.dat" /* fixed file name (read only) */
29 #define FILE_NAME_R2 "tfpif_r2.dat" /* fixed file name (read only) with a
30                                        precision > MPFR_PREC_MAX */
31 
32 /* Note: The perror below must be called just after the failing function,
33    thus before fprintf (otherwise one could get an error associated with
34    fprintf). */
35 
36 static void
37 doit (int argc, char *argv[], mpfr_prec_t p1, mpfr_prec_t p2)
38 {
39   const char *filenameCompressed = FILE_NAME_RW;
40   const char *data = FILE_NAME_R;
41   int status;
42   FILE *fh;
43   mpfr_t x[9];
44   mpfr_t y;
45   int i, neg;
46   long pos;
47 
48   mpfr_init2 (x[0], p1);
49   mpfr_init2 (x[8], p1);
50   mpfr_inits2 (p2, x[1], x[2], x[3], x[4], x[5], x[6], x[7], (mpfr_ptr) 0);
51   mpfr_set_str1 (x[0], "45.2564215000000018562786863185465335845947265625");
52   mpfr_set_str1 (x[1], "45.2564215000000018562786863185465335845947265625");
53   mpfr_set_str1 (x[2], "45.2564215000000018562786863185465335845947265625");
54   mpfr_set_exp (x[2], -48000);
55   mpfr_set_inf (x[3], 1);
56   mpfr_set_zero (x[4], 1);
57   mpfr_set_nan (x[5]);
58   mpfr_set_ui (x[6], 104348, MPFR_RNDN);
59   mpfr_set_ui (x[7], 33215, MPFR_RNDN);
60   mpfr_div (x[8], x[6], x[7], MPFR_RNDN);
61   mpfr_div (x[6], x[6], x[7], MPFR_RNDN);
62 
63   /* we first write to file FILE_NAME_RW the numbers x[i] */
64   fh = fopen (filenameCompressed, "w");
65   if (fh == NULL)
66     {
67       perror ("doit");
68       fprintf (stderr, "Failed to open \"%s\" for writing\n",
69                filenameCompressed);
70       exit (1);
71     }
72 
73   for (neg = 0; neg < 2; neg++)
74     for (i = 0; i < 9; i++)
75       {
76         if (neg)
77           MPFR_CHANGE_SIGN (x[i]);
78 
79         status = mpfr_fpif_export (fh, x[i]);
80         if (status != 0)
81           {
82             fclose (fh);
83             printf ("Failed to export number %d, neg=%d\n", i, neg);
84             exit (1);
85           }
86 
87         if (neg)
88           MPFR_CHANGE_SIGN (x[i]);
89       }
90 
91   if (fclose (fh) != 0)
92     {
93       perror ("doit");
94       fprintf (stderr, "Failed to close \"%s\"\n", filenameCompressed);
95       exit (1);
96     }
97 
98   /* we then read back FILE_NAME_RW and check we get the same numbers x[i] */
99   fh = fopen (filenameCompressed, "r");
100   if (fh == NULL)
101     {
102       perror ("doit");
103       fprintf (stderr, "Failed to open \"%s\" for reading\n",
104                filenameCompressed);
105       exit (1);
106     }
107 
108   for (neg = 0; neg < 2; neg++)
109     for (i = 0; i < 9; i++)
110       {
111         mpfr_prec_t px, py;
112 
113         if (neg)
114           MPFR_CHANGE_SIGN (x[i]);
115 
116         mpfr_init2 (y, 2);
117         /* Set the sign bit of y to the opposite of the expected one.
118            Thus, if mpfr_fpif_import forgets to set the sign, this will
119            be detected. */
120         MPFR_SET_SIGN (y, - MPFR_SIGN (x[i]));
121         mpfr_fpif_import (y, fh);
122         px = mpfr_get_prec (x[i]);
123         py = mpfr_get_prec (y);
124         if (px != py)
125           {
126             printf ("doit failed on written number %d, neg=%d:"
127                     " bad precision\n", i, neg);
128             printf ("expected %ld\n", (long) px);
129             printf ("got      %ld\n", (long) py);
130             exit (1);
131           }
132         if (MPFR_SIGN (x[i]) != MPFR_SIGN (y))
133           {
134             printf ("doit failed on written number %d, neg=%d:"
135                     " bad sign\n", i, neg);
136             printf ("expected %d\n", (int) MPFR_SIGN (x[i]));
137             printf ("got      %d\n", (int) MPFR_SIGN (y));
138             exit (1);
139           }
140         if (! SAME_VAL (x[i], y))
141           {
142             printf ("doit failed on written number %d, neg=%d\n", i, neg);
143             printf ("expected "); mpfr_dump (x[i]);
144             printf ("got      "); mpfr_dump (y);
145             exit (1);
146           }
147         mpfr_clear (y);
148 
149         if (neg)
150           MPFR_CHANGE_SIGN (x[i]);
151       }
152   fclose (fh);
153 
154   /* we do the same for the fixed file FILE_NAME_R, this ensures
155      we get same results with different word size or endianness */
156   fh = src_fopen (data, "r");
157   if (fh == NULL)
158     {
159       perror ("doit");
160       fprintf (stderr, "Failed to open \"%s\" in srcdir for reading\n", data);
161       exit (1);
162     }
163 
164   /* the fixed file FILE_NAME_R assumes p1=130 and p2=2048 */
165   for (i = 0; i < 9 && (p1 == 130 && p2 == 2048); i++)
166     {
167       mpfr_prec_t px, py;
168 
169       mpfr_init2 (y, 2);
170       /* Set the sign bit of y to the opposite of the expected one.
171          Thus, if mpfr_fpif_import forgets to set the sign, this will
172          be detected. */
173       MPFR_SET_SIGN (y, - MPFR_SIGN (x[i]));
174       pos = ftell (fh);
175       mpfr_fpif_import (y, fh);
176       px = mpfr_get_prec (x[i]);
177       py = mpfr_get_prec (y);
178       if (px != py)
179         {
180           printf ("doit failed on data number %d, neg=%d:"
181                   " bad precision\n", i, neg);
182           printf ("expected %ld\n", (long) px);
183           printf ("got      %ld\n", (long) py);
184           exit (1);
185         }
186       if (MPFR_SIGN (x[i]) != MPFR_SIGN (y))
187         {
188           printf ("doit failed on data number %d, neg=%d:"
189                   " bad sign\n", i, neg);
190           printf ("expected %d\n", (int) MPFR_SIGN (x[i]));
191           printf ("got      %d\n", (int) MPFR_SIGN (y));
192           exit (1);
193         }
194       if (! SAME_VAL (x[i], y))
195         {
196           printf ("doit failed on data number %d, neg=%d, at offset 0x%lx\n",
197                   i, neg, (unsigned long) pos);
198           printf ("expected "); mpfr_dump (x[i]);
199           printf ("got      "); mpfr_dump (y);
200           exit (1);
201         }
202       mpfr_clear (y);
203     }
204   fclose (fh);
205 
206   for (i = 0; i < 9; i++)
207     mpfr_clear (x[i]);
208 
209   remove (filenameCompressed);
210 }
211 
212 #define BAD 10
213 
214 static void
215 check_bad (void)
216 {
217   const char *filenameCompressed = FILE_NAME_RW;
218   int status;
219   FILE *fh;
220   mpfr_t x;
221   unsigned char badData[BAD][10] =
222     { { 7 }, { 16 }, { 23, 118 }, { 23, 95 }, { 23, 127 }, { 23, 47 },
223       { 7, 0, 0, 0, 0, 0, 0, 0, 128, 119 }, /* +0 in a huge precision */
224       /* precision 8-7=1, exponent on 98-94=4 bytes */
225       { 8, 98, 255, 255, 255, 127 },
226       /* precision 8-7=1, exponent on 102-94=8 bytes */
227       { 8, 102, 255, 255, 255, 255, 255, 255, 255, 127 },
228       { 8, 94 }
229       };
230   int badDataSize[BAD] = { 1, 1, 2, 2, 2, 2, 10, 6, 10, 2 };
231   int i;
232 
233   mpfr_init2 (x, 2);
234   status = mpfr_fpif_export (NULL, x);
235   if (status == 0)
236     {
237       printf ("mpfr_fpif_export did not fail with a NULL file\n");
238       exit (1);
239     }
240   status = mpfr_fpif_import (x, NULL);
241   if (status == 0)
242     {
243       printf ("mpfr_fpif_import did not fail with a NULL file\n");
244       exit (1);
245     }
246 
247   /* Since the file will be read after writing to it and a rewind, we need
248      to open it in mode "w+".
249      Note: mode "w" was used previously, and the issue remained undetected
250      until a test on AIX, where the fclose failed with the error:
251        check_bad: A file descriptor does not refer to an open file.
252      (the exit code of fclose has been checked since r13549 / 2019-08-09,
253      at the same time "w+" was changed to "w" by mistake).
254      What actually happened is that the fread in mpfr_fpif_import failed,
255      but this was not tested. So a test of errno has been added below;
256      with mode "w" (instead of "w+"), it yields:
257        check_bad: Bad file descriptor
258      as expected. */
259   fh = fopen (filenameCompressed, "w+");
260   if (fh == NULL)
261     {
262       perror ("check_bad");
263       fprintf (stderr, "Failed to open \"%s\" for writing\n",
264               filenameCompressed);
265       fclose (fh);
266       remove (filenameCompressed);
267       exit (1);
268     }
269   status = mpfr_fpif_import (x, fh);
270   if (status == 0)
271     {
272       printf ("mpfr_fpif_import did not fail on a empty file\n");
273       fclose (fh);
274       remove (filenameCompressed);
275       exit (1);
276     }
277 
278   for (i = 0; i < BAD; i++)
279     {
280       mpfr_exp_t emax;
281       /* For i == 6, mpfr_prec_t needs at least a 65-bit precision
282          (64 value bits + 1 sign bit) to avoid a failure. */
283       if (i == 6 && MPFR_PREC_BITS > 64)
284         break;
285       /* For i=9, we use a reduced exponent range */
286       if (i == 9)
287         {
288           emax = mpfr_get_emax ();
289           mpfr_set_emax (46);
290         }
291       rewind (fh);
292       status = fwrite (&badData[i][0], badDataSize[i], 1, fh);
293       if (status != 1)
294         {
295           printf ("Write error on the test file\n");
296           fclose (fh);
297           remove (filenameCompressed);
298           exit (1);
299         }
300       rewind (fh);
301       /* The check of errno below is needed to make sure that
302          mpfr_fpif_import fails due to bad data, not for some
303          arbitrary system error. */
304       errno = 0;
305       status = mpfr_fpif_import (x, fh);
306       if (errno != 0)
307         {
308           perror ("check_bad");
309           fprintf (stderr, "mpfr_fpif_import failed with unexpected"
310                    " errno = %d (and status = %d)\n", errno, status);
311           fclose (fh);
312           remove (filenameCompressed);
313           exit (1);
314         }
315       if (status == 0)
316         {
317           printf ("mpfr_fpif_import did not fail on a bad imported data\n");
318           switch (i)
319             {
320             case 0:
321               printf ("  not enough precision data\n");
322               break;
323             case 1:
324               printf ("  no exponent data\n");
325               break;
326             case 2:
327               printf ("  too big exponent\n");
328               break;
329             case 3:
330               printf ("  not enough exponent data\n");
331               break;
332             case 4:
333               printf ("  exponent data wrong\n");
334               break;
335             case 5:
336               printf ("  no limb data\n");
337               break;
338             case 6:
339               printf ("  too large precision\n");
340               break;
341             case 7:
342             case 8:
343             case 9:
344               printf ("  too large exponent\n");
345               break;
346             default:
347               printf ("Test fatal error, unknown case\n");
348               break;
349             }
350           fclose (fh);
351           remove (filenameCompressed);
352           exit (1);
353         }
354       if (i == 9)
355         mpfr_set_emax (emax);
356     }
357 
358   if (fclose (fh) != 0)
359     {
360       perror ("check_bad");
361       fprintf (stderr, "Failed to close \"%s\"\n", filenameCompressed);
362       exit (1);
363     }
364 
365   mpfr_clear (x);
366 
367   fh = fopen (filenameCompressed, "r");
368   if (fh == NULL)
369     {
370       perror ("check_bad");
371       fprintf (stderr, "Failed to open \"%s\" for reading\n",
372                filenameCompressed);
373       exit (1);
374     }
375 
376   mpfr_init2 (x, 2);
377   status = mpfr_fpif_export (fh, x);
378   if (status == 0)
379     {
380       printf ("mpfr_fpif_export did not fail on a read only stream\n");
381       exit (1);
382     }
383   fclose (fh);
384   remove (filenameCompressed);
385   mpfr_clear (x);
386 }
387 
388 /* exercise error when precision > MPFR_PREC_MAX */
389 static void
390 extra (void)
391 {
392   const char *data = FILE_NAME_R2;
393   mpfr_t x;
394   FILE *fp;
395   int ret;
396 
397   mpfr_init2 (x, 17);
398   mpfr_set_ui (x, 42, MPFR_RNDN);
399   fp = src_fopen (data, "r");
400   if (fp == NULL)
401     {
402       perror ("extra");
403       fprintf (stderr, "Failed to open \"%s\" in srcdir for reading\n", data);
404       exit (1);
405     }
406   ret = mpfr_fpif_import (x, fp);
407   MPFR_ASSERTN (ret != 0);  /* import failure */
408   MPFR_ASSERTN (mpfr_get_prec (x) == 17);  /* precision did not change */
409   MPFR_ASSERTN (mpfr_cmp_ui0 (x, 42) == 0);  /* value is still 42 */
410   fclose (fp);
411   mpfr_clear (x);
412 }
413 
414 int
415 main (int argc, char *argv[])
416 {
417   if (argc != 1)
418     {
419       printf ("Usage: %s\n", argv[0]);
420       exit (1);
421     }
422 
423   tests_start_mpfr ();
424 
425   extra ();
426   doit (argc, argv, 130, 2048);
427   doit (argc, argv, 1, 53);
428   check_bad ();
429 
430   tests_end_mpfr ();
431 
432   return 0;
433 }
434