/* Test mpq_get_d and mpq_set_d
Copyright 1991, 1993, 1994, 1996, 2000-2003, 2012, 2013 Free Software
Foundation, Inc.
This file is part of the GNU MP Library test suite.
The GNU MP Library test suite is free software; you can redistribute it
and/or modify it under the terms of the GNU 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 MP Library test suite 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 General
Public License for more details.
You should have received a copy of the GNU General Public License along with
the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */
#include <stdio.h>
#include <stdlib.h>
#include "gmp-impl.h"
#include "tests.h"
#ifndef SIZE
#define SIZE 8
#endif
/* VAX D floats only have an 8 bit signed exponent, so anything 2^128 or
bigger will overflow, that being 4 limbs. */
#if defined (__vax) || defined (__vax__) && SIZE > 4
#undef SIZE
#define SIZE 4
#define EPSIZE 3
#else
#define EPSIZE SIZE
#endif
void dump (mpq_t);
void
check_monotonic (int argc, char **argv)
{
mpq_t a;
mp_size_t size;
int reps = 100;
int i, j;
double last_d, new_d;
mpq_t qlast_d, qnew_d;
mpq_t eps;
if (argc == 2)
reps = atoi (argv[1]);
/* The idea here is to test the monotonousness of mpq_get_d by adding
numbers to the numerator and denominator. */
mpq_init (a);
mpq_init (eps);
mpq_init (qlast_d);
mpq_init (qnew_d);
for (i = 0; i < reps; i++)
{
size = urandom () % SIZE - SIZE/2;
mpz_random2 (mpq_numref (a), size);
do
{
size = urandom () % SIZE - SIZE/2;
mpz_random2 (mpq_denref (a), size);
}
while (mpz_cmp_ui (mpq_denref (a), 0) == 0);
mpq_canonicalize (a);
last_d = mpq_get_d (a);
mpq_set_d (qlast_d, last_d);
for (j = 0; j < 10; j++)
{
size = urandom () % EPSIZE + 1;
mpz_random2 (mpq_numref (eps), size);
size = urandom () % EPSIZE + 1;
mpz_random2 (mpq_denref (eps), size);
mpq_canonicalize (eps);
mpq_add (a, a, eps);
mpq_canonicalize (a);
new_d = mpq_get_d (a);
if (last_d > new_d)
{
printf ("\nERROR (test %d/%d): bad mpq_get_d results\n", i, j);
printf ("last: %.16g\n", last_d);
printf (" new: %.16g\n", new_d); dump (a);
abort ();
}
mpq_set_d (qnew_d, new_d);
MPQ_CHECK_FORMAT (qnew_d);
if (mpq_cmp (qlast_d, qnew_d) > 0)
{
printf ("ERROR (test %d/%d): bad mpq_set_d results\n", i, j);
printf ("last: %.16g\n", last_d); dump (qlast_d);
printf (" new: %.16g\n", new_d); dump (qnew_d);
abort ();
}
last_d = new_d;
mpq_set (qlast_d, qnew_d);
}
}
mpq_clear (a);
mpq_clear (eps);
mpq_clear (qlast_d);
mpq_clear (qnew_d);
}
double
my_ldexp (double d, int e)
{
for (;;)
{
if (e > 0)
{
if (e >= 16)
{
d *= 65536.0;
e -= 16;
}
else
{
d *= 2.0;
e -= 1;
}
}
else if (e < 0)
{
if (e <= -16)
{
d /= 65536.0;
e += 16;
}
else
{
d /= 2.0;
e += 1;
}
}
else
return d;
}
}
#define MAXEXP 500
#if defined (__vax) || defined (__vax__)
#undef MAXEXP
#define MAXEXP 30
#endif
void
check_random (int argc, char **argv)
{
gmp_randstate_ptr rands = RANDS;
double d;
mpq_t q;
mpz_t a, t;
int exp;
int test, reps = 100000;
if (argc == 2)
reps = 100 * atoi (argv[1]);
mpq_init (q);
mpz_init (a);
mpz_init (t);
for (test = 0; test < reps; test++)
{
mpz_rrandomb (a, rands, 53);
mpz_urandomb (t, rands, 32);
exp = mpz_get_ui (t) % (2*MAXEXP) - MAXEXP;
d = my_ldexp (mpz_get_d (a), exp);
mpq_set_d (q, d);
/* Check that n/d = a * 2^exp, or
d*a 2^{exp} = n */
mpz_mul (t, a, mpq_denref (q));
if (exp > 0)
mpz_mul_2exp (t, t, exp);
else
{
if (!mpz_divisible_2exp_p (t, -exp))
goto fail;
mpz_div_2exp (t, t, -exp);
}
if (mpz_cmp (t, mpq_numref (q)) != 0)
{
fail:
printf ("ERROR (check_random test %d): bad mpq_set_d results\n", test);
printf ("%.16g\n", d);
gmp_printf ("%Qd\n", q);
abort ();
}
}
mpq_clear (q);
mpz_clear (t);
mpz_clear (a);
}
void
dump (mpq_t x)
{
mpz_out_str (stdout, 10, mpq_numref (x));
printf ("/");
mpz_out_str (stdout, 10, mpq_denref (x));
printf ("\n");
}
/* Check various values 2^n and 1/2^n. */
void
check_onebit (void)
{
static const long data[] = {
-3*GMP_NUMB_BITS-1, -3*GMP_NUMB_BITS, -3*GMP_NUMB_BITS+1,
-2*GMP_NUMB_BITS-1, -2*GMP_NUMB_BITS, -2*GMP_NUMB_BITS+1,
-GMP_NUMB_BITS-1, -GMP_NUMB_BITS, -GMP_NUMB_BITS+1,
-5, -2, -1, 0, 1, 2, 5,
GMP_NUMB_BITS-1, GMP_NUMB_BITS, GMP_NUMB_BITS+1,
2*GMP_NUMB_BITS-1, 2*GMP_NUMB_BITS, 2*GMP_NUMB_BITS+1,
3*GMP_NUMB_BITS-1, 3*GMP_NUMB_BITS, 3*GMP_NUMB_BITS+1,
};
int i, neg;
long exp, l;
mpq_t q;
double got, want;
mpq_init (q);
for (i = 0; i < numberof (data); i++)
{
exp = data[i];
mpq_set_ui (q, 1L, 1L);
if (exp >= 0)
mpq_mul_2exp (q, q, exp);
else
mpq_div_2exp (q, q, -exp);
want = 1.0;
for (l = 0; l < exp; l++)
want *= 2.0;
for (l = 0; l > exp; l--)
want /= 2.0;
for (neg = 0; neg <= 1; neg++)
{
if (neg)
{
mpq_neg (q, q);
want = -want;
}
got = mpq_get_d (q);
if (got != want)
{
printf ("mpq_get_d wrong on %s2**%ld\n", neg ? "-" : "", exp);
mpq_trace (" q ", q);
d_trace (" want ", want);
d_trace (" got ", got);
abort();
}
}
}
mpq_clear (q);
}
int
main (int argc, char **argv)
{
tests_start ();
check_onebit ();
check_monotonic (argc, argv);
check_random (argc, argv);
tests_end ();
exit (0);
}