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