1/* Test file for mpfr_fpif. 2 3Copyright 2012-2023 Free Software Foundation, Inc. 4Contributed by Olivier Demengeon. 5 6This file is part of the GNU MPFR Library. 7 8The GNU MPFR Library is free software; you can redistribute it and/or modify 9it under the terms of the GNU Lesser General Public License as published by 10the Free Software Foundation; either version 3 of the License, or (at your 11option) any later version. 12 13The GNU MPFR Library is distributed in the hope that it will be useful, but 14WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 16License for more details. 17 18You should have received a copy of the GNU Lesser General Public License 19along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see 20https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., 2151 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 36static void 37doit (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 214static void 215check_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 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 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 */ 389static void 390extra (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 414int 415main (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