Home | History | Annotate | Line # | Download | only in tests
      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
     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       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
    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
    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