/* Test file for mpfr_fpif.
Copyright 2012-2023 Free Software Foundation, Inc.
Contributed by Olivier Demengeon.
This file is part of the GNU MPFR Library.
The GNU MPFR Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or (at your
option) any later version.
The GNU MPFR Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see
https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
#include <errno.h>
#include "mpfr-test.h"
#define FILE_NAME_RW "tfpif_rw.dat" /* temporary name (written then read) */
#define FILE_NAME_R "tfpif_r1.dat" /* fixed file name (read only) */
#define FILE_NAME_R2 "tfpif_r2.dat" /* fixed file name (read only) with a
precision > MPFR_PREC_MAX */
/* Note: The perror below must be called just after the failing function,
thus before fprintf (otherwise one could get an error associated with
fprintf). */
static void
doit (int argc, char *argv[], mpfr_prec_t p1, mpfr_prec_t p2)
{
const char *filenameCompressed = FILE_NAME_RW;
const char *data = FILE_NAME_R;
int status;
FILE *fh;
mpfr_t x[9];
mpfr_t y;
int i, neg;
long pos;
mpfr_init2 (x[0], p1);
mpfr_init2 (x[8], p1);
mpfr_inits2 (p2, x[1], x[2], x[3], x[4], x[5], x[6], x[7], (mpfr_ptr) 0);
mpfr_set_str1 (x[0], "45.2564215000000018562786863185465335845947265625");
mpfr_set_str1 (x[1], "45.2564215000000018562786863185465335845947265625");
mpfr_set_str1 (x[2], "45.2564215000000018562786863185465335845947265625");
mpfr_set_exp (x[2], -48000);
mpfr_set_inf (x[3], 1);
mpfr_set_zero (x[4], 1);
mpfr_set_nan (x[5]);
mpfr_set_ui (x[6], 104348, MPFR_RNDN);
mpfr_set_ui (x[7], 33215, MPFR_RNDN);
mpfr_div (x[8], x[6], x[7], MPFR_RNDN);
mpfr_div (x[6], x[6], x[7], MPFR_RNDN);
/* we first write to file FILE_NAME_RW the numbers x[i] */
fh = fopen (filenameCompressed, "w");
if (fh == NULL)
{
perror ("doit");
fprintf (stderr, "Failed to open \"%s\" for writing\n",
filenameCompressed);
exit (1);
}
for (neg = 0; neg < 2; neg++)
for (i = 0; i < 9; i++)
{
if (neg)
MPFR_CHANGE_SIGN (x[i]);
status = mpfr_fpif_export (fh, x[i]);
if (status != 0)
{
fclose (fh);
printf ("Failed to export number %d, neg=%d\n", i, neg);
exit (1);
}
if (neg)
MPFR_CHANGE_SIGN (x[i]);
}
if (fclose (fh) != 0)
{
perror ("doit");
fprintf (stderr, "Failed to close \"%s\"\n", filenameCompressed);
exit (1);
}
/* we then read back FILE_NAME_RW and check we get the same numbers x[i] */
fh = fopen (filenameCompressed, "r");
if (fh == NULL)
{
perror ("doit");
fprintf (stderr, "Failed to open \"%s\" for reading\n",
filenameCompressed);
exit (1);
}
for (neg = 0; neg < 2; neg++)
for (i = 0; i < 9; i++)
{
mpfr_prec_t px, py;
if (neg)
MPFR_CHANGE_SIGN (x[i]);
mpfr_init2 (y, 2);
/* Set the sign bit of y to the opposite of the expected one.
Thus, if mpfr_fpif_import forgets to set the sign, this will
be detected. */
MPFR_SET_SIGN (y, - MPFR_SIGN (x[i]));
mpfr_fpif_import (y, fh);
px = mpfr_get_prec (x[i]);
py = mpfr_get_prec (y);
if (px != py)
{
printf ("doit failed on written number %d, neg=%d:"
" bad precision\n", i, neg);
printf ("expected %ld\n", (long) px);
printf ("got %ld\n", (long) py);
exit (1);
}
if (MPFR_SIGN (x[i]) != MPFR_SIGN (y))
{
printf ("doit failed on written number %d, neg=%d:"
" bad sign\n", i, neg);
printf ("expected %d\n", (int) MPFR_SIGN (x[i]));
printf ("got %d\n", (int) MPFR_SIGN (y));
exit (1);
}
if (! SAME_VAL (x[i], y))
{
printf ("doit failed on written number %d, neg=%d\n", i, neg);
printf ("expected "); mpfr_dump (x[i]);
printf ("got "); mpfr_dump (y);
exit (1);
}
mpfr_clear (y);
if (neg)
MPFR_CHANGE_SIGN (x[i]);
}
fclose (fh);
/* we do the same for the fixed file FILE_NAME_R, this ensures
we get same results with different word size or endianness */
fh = src_fopen (data, "r");
if (fh == NULL)
{
perror ("doit");
fprintf (stderr, "Failed to open \"%s\" in srcdir for reading\n", data);
exit (1);
}
/* the fixed file FILE_NAME_R assumes p1=130 and p2=2048 */
for (i = 0; i < 9 && (p1 == 130 && p2 == 2048); i++)
{
mpfr_prec_t px, py;
mpfr_init2 (y, 2);
/* Set the sign bit of y to the opposite of the expected one.
Thus, if mpfr_fpif_import forgets to set the sign, this will
be detected. */
MPFR_SET_SIGN (y, - MPFR_SIGN (x[i]));
pos = ftell (fh);
mpfr_fpif_import (y, fh);
px = mpfr_get_prec (x[i]);
py = mpfr_get_prec (y);
if (px != py)
{
printf ("doit failed on data number %d, neg=%d:"
" bad precision\n", i, neg);
printf ("expected %ld\n", (long) px);
printf ("got %ld\n", (long) py);
exit (1);
}
if (MPFR_SIGN (x[i]) != MPFR_SIGN (y))
{
printf ("doit failed on data number %d, neg=%d:"
" bad sign\n", i, neg);
printf ("expected %d\n", (int) MPFR_SIGN (x[i]));
printf ("got %d\n", (int) MPFR_SIGN (y));
exit (1);
}
if (! SAME_VAL (x[i], y))
{
printf ("doit failed on data number %d, neg=%d, at offset 0x%lx\n",
i, neg, (unsigned long) pos);
printf ("expected "); mpfr_dump (x[i]);
printf ("got "); mpfr_dump (y);
exit (1);
}
mpfr_clear (y);
}
fclose (fh);
for (i = 0; i < 9; i++)
mpfr_clear (x[i]);
remove (filenameCompressed);
}
#define BAD 10
static void
check_bad (void)
{
const char *filenameCompressed = FILE_NAME_RW;
int status;
FILE *fh;
mpfr_t x;
unsigned char badData[BAD][10] =
{ { 7 }, { 16 }, { 23, 118 }, { 23, 95 }, { 23, 127 }, { 23, 47 },
{ 7, 0, 0, 0, 0, 0, 0, 0, 128, 119 }, /* +0 in a huge precision */
/* precision 8-7=1, exponent on 98-94=4 bytes */
{ 8, 98, 255, 255, 255, 127 },
/* precision 8-7=1, exponent on 102-94=8 bytes */
{ 8, 102, 255, 255, 255, 255, 255, 255, 255, 127 },
{ 8, 94 }
};
int badDataSize[BAD] = { 1, 1, 2, 2, 2, 2, 10, 6, 10, 2 };
int i;
mpfr_init2 (x, 2);
status = mpfr_fpif_export (NULL, x);
if (status == 0)
{
printf ("mpfr_fpif_export did not fail with a NULL file\n");
exit (1);
}
status = mpfr_fpif_import (x, NULL);
if (status == 0)
{
printf ("mpfr_fpif_import did not fail with a NULL file\n");
exit (1);
}
/* Since the file will be read after writing to it and a rewind, we need
to open it in mode "w+".
Note: mode "w" was used previously, and the issue remained undetected
until a test on AIX, where the fclose failed with the error:
check_bad: A file descriptor does not refer to an open file.
(the exit code of fclose has been checked since r13549 / 2019-08-09,
at the same time "w+" was changed to "w" by mistake).
What actually happened is that the fread in mpfr_fpif_import failed,
but this was not tested. So a test of errno has been added below;
with mode "w" (instead of "w+"), it yields:
check_bad: Bad file descriptor
as expected. */
fh = fopen (filenameCompressed, "w+");
if (fh == NULL)
{
perror ("check_bad");
fprintf (stderr, "Failed to open \"%s\" for writing\n",
filenameCompressed);
fclose (fh);
remove (filenameCompressed);
exit (1);
}
status = mpfr_fpif_import (x, fh);
if (status == 0)
{
printf ("mpfr_fpif_import did not fail on a empty file\n");
fclose (fh);
remove (filenameCompressed);
exit (1);
}
for (i = 0; i < BAD; i++)
{
mpfr_exp_t emax;
/* For i == 6, mpfr_prec_t needs at least a 65-bit precision
(64 value bits + 1 sign bit) to avoid a failure. */
if (i == 6 && MPFR_PREC_BITS > 64)
break;
/* For i=9, we use a reduced exponent range */
if (i == 9)
{
emax = mpfr_get_emax ();
set_emax (46);
}
rewind (fh);
status = fwrite (&badData[i][0], badDataSize[i], 1, fh);
if (status != 1)
{
printf ("Write error on the test file\n");
fclose (fh);
remove (filenameCompressed);
exit (1);
}
rewind (fh);
/* The check of errno below is needed to make sure that
mpfr_fpif_import fails due to bad data, not for some
arbitrary system error. */
errno = 0;
status = mpfr_fpif_import (x, fh);
if (errno != 0)
{
perror ("check_bad");
fprintf (stderr, "mpfr_fpif_import failed with unexpected"
" errno = %d (and status = %d)\n", errno, status);
fclose (fh);
remove (filenameCompressed);
exit (1);
}
if (status == 0)
{
printf ("mpfr_fpif_import did not fail on a bad imported data\n");
switch (i)
{
case 0:
printf (" not enough precision data\n");
break;
case 1:
printf (" no exponent data\n");
break;
case 2:
printf (" too big exponent\n");
break;
case 3:
printf (" not enough exponent data\n");
break;
case 4:
printf (" exponent data wrong\n");
break;
case 5:
printf (" no limb data\n");
break;
case 6:
printf (" too large precision\n");
break;
case 7:
case 8:
case 9:
printf (" too large exponent\n");
break;
default:
printf ("Test fatal error, unknown case\n");
break;
}
fclose (fh);
remove (filenameCompressed);
exit (1);
}
if (i == 9)
set_emax (emax);
}
if (fclose (fh) != 0)
{
perror ("check_bad");
fprintf (stderr, "Failed to close \"%s\"\n", filenameCompressed);
exit (1);
}
mpfr_clear (x);
fh = fopen (filenameCompressed, "r");
if (fh == NULL)
{
perror ("check_bad");
fprintf (stderr, "Failed to open \"%s\" for reading\n",
filenameCompressed);
exit (1);
}
mpfr_init2 (x, 2);
status = mpfr_fpif_export (fh, x);
if (status == 0)
{
printf ("mpfr_fpif_export did not fail on a read only stream\n");
exit (1);
}
fclose (fh);
remove (filenameCompressed);
mpfr_clear (x);
}
/* exercise error when precision > MPFR_PREC_MAX */
static void
extra (void)
{
const char *data = FILE_NAME_R2;
mpfr_t x;
FILE *fp;
int ret;
mpfr_init2 (x, 17);
mpfr_set_ui (x, 42, MPFR_RNDN);
fp = src_fopen (data, "r");
if (fp == NULL)
{
perror ("extra");
fprintf (stderr, "Failed to open \"%s\" in srcdir for reading\n", data);
exit (1);
}
ret = mpfr_fpif_import (x, fp);
MPFR_ASSERTN (ret != 0); /* import failure */
MPFR_ASSERTN (mpfr_get_prec (x) == 17); /* precision did not change */
MPFR_ASSERTN (mpfr_cmp_ui0 (x, 42) == 0); /* value is still 42 */
fclose (fp);
mpfr_clear (x);
}
int
main (int argc, char *argv[])
{
if (argc != 1)
{
printf ("Usage: %s\n", argv[0]);
exit (1);
}
tests_start_mpfr ();
extra ();
doit (argc, argv, 130, 2048);
doit (argc, argv, 1, 53);
check_bad ();
tests_end_mpfr ();
return 0;
}