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