xref: /netbsd-src/external/lgpl3/mpfr/dist/tests/tfpif.c (revision ec6772edaf0cdcb5f52a48f4aca5e33a8fb8ecfd)
1 /* Test file for mpfr_fpif.
2 
3 Copyright 2012-2023 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
doit(int argc,char * argv[],mpfr_prec_t p1,mpfr_prec_t p2)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
check_bad(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       remove (filenameCompressed);
266       exit (1);
267     }
268   status = mpfr_fpif_import (x, fh);
269   if (status == 0)
270     {
271       printf ("mpfr_fpif_import did not fail on a empty file\n");
272       fclose (fh);
273       remove (filenameCompressed);
274       exit (1);
275     }
276 
277   for (i = 0; i < BAD; i++)
278     {
279       mpfr_exp_t INITIALIZED(emax);
280       /* The INITIALIZED() is a workaround for GCC bug 106155:
281          https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106155 */
282 
283       /* For i == 6, mpfr_prec_t needs at least a 65-bit precision
284          (64 value bits + 1 sign bit) to avoid a failure. */
285       if (i == 6 && MPFR_PREC_BITS > 64)
286         break;
287       /* For i=9, we use a reduced exponent range */
288       if (i == 9)
289         {
290           emax = mpfr_get_emax ();
291           set_emax (46);
292         }
293       rewind (fh);
294       status = fwrite (&badData[i][0], badDataSize[i], 1, fh);
295       if (status != 1)
296         {
297           printf ("Write error on the test file\n");
298           fclose (fh);
299           remove (filenameCompressed);
300           exit (1);
301         }
302       rewind (fh);
303       /* The check of errno below is needed to make sure that
304          mpfr_fpif_import fails due to bad data, not for some
305          arbitrary system error. */
306       errno = 0;
307       status = mpfr_fpif_import (x, fh);
308       if (errno != 0)
309         {
310           perror ("check_bad");
311           fprintf (stderr, "mpfr_fpif_import failed with unexpected"
312                    " errno = %d (and status = %d)\n", errno, status);
313           fclose (fh);
314           remove (filenameCompressed);
315           exit (1);
316         }
317       if (status == 0)
318         {
319           printf ("mpfr_fpif_import did not fail on a bad imported data\n");
320           switch (i)
321             {
322             case 0:
323               printf ("  not enough precision data\n");
324               break;
325             case 1:
326               printf ("  no exponent data\n");
327               break;
328             case 2:
329               printf ("  too big exponent\n");
330               break;
331             case 3:
332               printf ("  not enough exponent data\n");
333               break;
334             case 4:
335               printf ("  exponent data wrong\n");
336               break;
337             case 5:
338               printf ("  no limb data\n");
339               break;
340             case 6:
341               printf ("  too large precision\n");
342               break;
343             case 7:
344             case 8:
345             case 9:
346               printf ("  too large exponent\n");
347               break;
348             default:
349               printf ("Test fatal error, unknown case\n");
350               break;
351             }
352           fclose (fh);
353           remove (filenameCompressed);
354           exit (1);
355         }
356       if (i == 9)
357         set_emax (emax);
358     }
359 
360   if (fclose (fh) != 0)
361     {
362       perror ("check_bad");
363       fprintf (stderr, "Failed to close \"%s\"\n", filenameCompressed);
364       exit (1);
365     }
366 
367   mpfr_clear (x);
368 
369   fh = fopen (filenameCompressed, "r");
370   if (fh == NULL)
371     {
372       perror ("check_bad");
373       fprintf (stderr, "Failed to open \"%s\" for reading\n",
374                filenameCompressed);
375       exit (1);
376     }
377 
378   mpfr_init2 (x, 2);
379   status = mpfr_fpif_export (fh, x);
380   if (status == 0)
381     {
382       printf ("mpfr_fpif_export did not fail on a read only stream\n");
383       exit (1);
384     }
385   fclose (fh);
386   remove (filenameCompressed);
387   mpfr_clear (x);
388 }
389 
390 /* exercise error when precision > MPFR_PREC_MAX */
391 static void
extra(void)392 extra (void)
393 {
394   const char *data = FILE_NAME_R2;
395   mpfr_t x;
396   FILE *fp;
397   int ret;
398 
399   mpfr_init2 (x, 17);
400   mpfr_set_ui (x, 42, MPFR_RNDN);
401   fp = src_fopen (data, "r");
402   if (fp == NULL)
403     {
404       perror ("extra");
405       fprintf (stderr, "Failed to open \"%s\" in srcdir for reading\n", data);
406       exit (1);
407     }
408   ret = mpfr_fpif_import (x, fp);
409   MPFR_ASSERTN (ret != 0);  /* import failure */
410   MPFR_ASSERTN (mpfr_get_prec (x) == 17);  /* precision did not change */
411   MPFR_ASSERTN (mpfr_cmp_ui0 (x, 42) == 0);  /* value is still 42 */
412   fclose (fp);
413   mpfr_clear (x);
414 }
415 
416 int
main(int argc,char * argv[])417 main (int argc, char *argv[])
418 {
419   if (argc != 1)
420     {
421       printf ("Usage: %s\n", argv[0]);
422       exit (1);
423     }
424 
425   tests_start_mpfr ();
426 
427   extra ();
428   doit (argc, argv, 130, 2048);
429   doit (argc, argv, 1, 53);
430   check_bad ();
431 
432   tests_end_mpfr ();
433 
434   return 0;
435 }
436