/* 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 #include #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); }