/* Test file for mpfr_random_deviate Copyright 2011-2021 Free Software Foundation, Inc. Contributed by Charles Karney , SRI International. 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 "mpfr-test.h" #include "random_deviate.h" #define W 32 /* Must match value in random_deviate.c */ /* set random deviate rop from op */ static void mpfr_random_deviate_set (mpfr_random_deviate_t rop, mpfr_random_deviate_t op) { rop->e = op->e; rop->h = op->h; mpz_set (rop->f, op->f); } /* set random deviate to fract * 2^-expt. expt must be a multiple * of W and cannot be 0. fract must be in [0,2^W) */ static void mpfr_random_deviate_ldexp (mpfr_random_deviate_t rop, unsigned long fract, unsigned long expt) { rop->h = (expt > W ? 0ul : fract); mpz_set_ui (rop->f, expt > W ? fract : 0ul); rop->e = expt; } /* Test mpfr_random_deviate_less. With two initially equal deviates this * should return true half the time. In order to execute additional code * paths, the two deviates are repeatedly set equal and the test repeated (with * now a longer fraction and with the test now triggering the sampling of an * additional chunk. */ static void test_compare (long nbtests, int verbose) { mpfr_random_deviate_t u, v; int k, i, t1, t2; long count; mpfr_random_deviate_init (u); mpfr_random_deviate_init (v); count = 0; for (k = 0; k < nbtests; ++k) { mpfr_random_deviate_reset (u); mpfr_random_deviate_reset (v); for (i = 0; i < 10; ++i) { t1 = mpfr_random_deviate_less (u, v, RANDS); t2 = mpfr_random_deviate_less (u, v, RANDS); if (t1 != t2) { printf ("Error: mpfr_random_deviate_less() inconsistent.\n"); exit (1); } if (t1) ++count; /* Force the test to sample an additional chunk */ mpfr_random_deviate_set (u, v); } t1 = mpfr_random_deviate_less (u, u, RANDS); if (t1) { printf ("Error: mpfr_random_deviate_less() gives u < u.\n"); exit (1); } t1 = mpfr_random_deviate_tstbit (u, 0, RANDS); if (t1) { printf ("Error: mpfr_random_deviate_tstbit() says 1 bit is on.\n"); exit (1); } } mpfr_random_deviate_clear (v); mpfr_random_deviate_clear (u); if (verbose) printf ("Fraction of true random_deviate_less = %.4f" " (should be about 0.5)\n", count / (double) (10 * nbtests)); } /* A random_deviate should consistently return the same value at a given * precision, even if intervening operations have caused the fraction to be * extended. */ static void test_value_consistency (long nbtests) { mpfr_t a1, a2, a3, b1, b2, b3; mpfr_random_deviate_t u; int inexa1, inexa2, inexa3, inexb1, inexb2, inexb3; mpfr_prec_t prec1, prec2, prec3; mpfr_rnd_t rnd; long i; unsigned n; int neg; /* Pick prec{1,2,3} random in [2,101] */ prec1 = 2 + gmp_urandomm_ui (RANDS, 100); prec2 = 2 + gmp_urandomm_ui (RANDS, 100); prec3 = 2 + gmp_urandomm_ui (RANDS, 100); rnd = MPFR_RNDN; mpfr_random_deviate_init (u); mpfr_init2 (a1, prec1); mpfr_init2 (b1, prec1); mpfr_init2 (a2, prec2); mpfr_init2 (b2, prec2); mpfr_init2 (a3, prec3); mpfr_init2 (b3, prec3); for (i = 0; i < nbtests; ++i) { mpfr_random_deviate_reset (u); n = gmp_urandomb_ui (RANDS, 4); neg = gmp_urandomb_ui (RANDS, 1); inexa1 = mpfr_random_deviate_value (neg, n, u, a1, RANDS, rnd); inexa2 = mpfr_random_deviate_value (neg, n, u, a2, RANDS, rnd); inexa3 = mpfr_random_deviate_value (neg, n, u, a3, RANDS, rnd); inexb1 = mpfr_random_deviate_value (neg, n, u, b1, RANDS, rnd); inexb2 = mpfr_random_deviate_value (neg, n, u, b2, RANDS, rnd); inexb3 = mpfr_random_deviate_value (neg, n, u, b3, RANDS, rnd); /* Of course a1, a2, and a3 should all be nearly equal. But more * crucially (and easier to test), we need a1 == b1, etc. (This is not a * trivial issue because the detailed mpfr operations giving b1 will be * different than for a1, if, e.g., prec2 > prec1. */ if ( !( inexa1 == inexb1 && mpfr_equal_p (a1, b1) && inexa2 == inexb2 && mpfr_equal_p (a2, b2) && inexa3 == inexb3 && mpfr_equal_p (a3, b3) ) ) { printf ("Error: random_deviate values are inconsistent.\n"); exit (1); } } mpfr_random_deviate_clear (u); mpfr_clears (a1, a2, a3, b1, b2, b3, (mpfr_ptr) 0); } /* Check that the values from random_deviate with different rounding modes are * consistent. */ static void test_value_round (long nbtests, mpfr_prec_t prec) { mpfr_t xn, xd, xu, xz, xa, t; mpfr_random_deviate_t u; int inexn, inexd, inexu, inexz, inexa, inext; long i; unsigned n; int neg, s; mpfr_random_deviate_init (u); mpfr_inits2 (prec, xn, xd, xu, xz, xa, t, (mpfr_ptr) 0); for (i = 0; i < nbtests; ++i) { mpfr_random_deviate_reset (u); n = gmp_urandomb_ui (RANDS, 4); neg = gmp_urandomb_ui (RANDS, 1); s = neg ? -1 : 1; inexn = mpfr_random_deviate_value (neg, n, u, xn, RANDS, MPFR_RNDN); inexd = mpfr_random_deviate_value (neg, n, u, xd, RANDS, MPFR_RNDD); inexu = mpfr_random_deviate_value (neg, n, u, xu, RANDS, MPFR_RNDU); inexz = mpfr_random_deviate_value (neg, n, u, xz, RANDS, MPFR_RNDZ); inexa = mpfr_random_deviate_value (neg, n, u, xa, RANDS, MPFR_RNDA); inext = mpfr_set (t, xn, MPFR_RNDN); /* Check inexact values */ if ( !( inexn != 0 && inext == 0 && inexd < 0 && inexu > 0 && inexz * s < 0 && inexa * s > 0 ) ) { printf ("n %d t %d d %d u %d z %d a %d s %d\n", inexn, inext, inexd, inexu, inexz, inexa, s); printf ("Error: random_deviate has wrong values for inexact.\n"); exit (1); } if (inexn < 0) mpfr_nextabove (t); else mpfr_nextbelow (t); /* Check that x{d,u,z,a} == xn is the inexact flags match, else * x{d,u,z,a} == t */ if ( !( mpfr_equal_p(xd, SAME_SIGN(inexn, inexd) ? xn : t) && mpfr_equal_p(xu, SAME_SIGN(inexn, inexu) ? xn : t) && mpfr_equal_p(xz, SAME_SIGN(inexn, inexz) ? xn : t) && mpfr_equal_p(xa, SAME_SIGN(inexn, inexa) ? xn : t) ) ) { printf ("n %d t %d d %d u %d z %d a %d s %d\n", inexn, inext, inexd, inexu, inexz, inexa, s); printf ("n %.4f t %.4f d %.4f u %.4f z %.4f a %.4f\n", mpfr_get_d (xn, MPFR_RNDN), mpfr_get_d (t, MPFR_RNDN), mpfr_get_d (xd, MPFR_RNDN), mpfr_get_d (xu, MPFR_RNDN), mpfr_get_d (xz, MPFR_RNDN), mpfr_get_d (xa, MPFR_RNDN)); printf ("Error: random_deviate rounds inconsistently.\n"); exit (1); } } mpfr_random_deviate_clear (u); mpfr_clears (xn, xd, xu, xz, xa, t, (mpfr_ptr) 0); } /* Test mpfr_random_deviate_value. Check for the leading bit in the number in * various positions. */ static void test_value (long nbtests, mpfr_prec_t prec, mpfr_rnd_t rnd, int verbose) { mpfr_t x; mpfr_random_deviate_t u; int inexact, exact; int i, k, b, neg; unsigned long e, f, n; long count, sum; mpfr_random_deviate_init (u); mpfr_init2 (x, prec); count = 0; sum = 0; exact = 0; for (k = 0; k < nbtests; ++k) { for (i = 0; i < 32; ++i) { b = gmp_urandomm_ui (RANDS, 32) + 1; /* bits to sample in integer */ n = gmp_urandomb_ui (RANDS, b); neg = gmp_urandomb_ui (RANDS, 1); inexact = mpfr_random_deviate_value (neg, n, u, x, RANDS, rnd); if (!inexact) exact = 1; if (inexact > 0) ++count; ++sum; } for (i = 0; i < 32; ++i) { b = gmp_urandomm_ui (RANDS, W) + 1; /* bits to sample in fraction */ f = gmp_urandomb_ui (RANDS, b); e = W * (gmp_urandomm_ui (RANDS, 3) + 1); mpfr_random_deviate_ldexp (u, f, e); neg = gmp_urandomb_ui (RANDS, 1); inexact = mpfr_random_deviate_value (neg, 0, u, x, RANDS, rnd); if (!inexact) exact = 1; if (inexact > 0) ++count; ++sum; } if (exact) { printf ("Error: random_deviate() returns a zero ternary value.\n"); exit (1); } mpfr_random_deviate_reset (u); } mpfr_random_deviate_clear (u); mpfr_clear (x); if (verbose) { printf ("Fraction of inexact > 0 = %.4f", count / (double) (sum)); if (rnd == MPFR_RNDD) printf (" should be exactly 0\n"); else if (rnd == MPFR_RNDU) printf (" should be exactly 1\n"); else printf (" should be about 0.5\n"); } } int main (int argc, char *argv[]) { long nbtests; int verbose; long a; tests_start_mpfr (); verbose = 0; nbtests = 10; if (argc > 1) { a = atol (argv[1]); verbose = 1; if (a != 0) nbtests = a; } test_compare (nbtests, verbose); test_value_consistency (nbtests); test_value_round (nbtests, 2); test_value_round (nbtests, 64); test_value (nbtests, 2, MPFR_RNDD, verbose); test_value (nbtests, 5, MPFR_RNDU, verbose); test_value (nbtests, 24, MPFR_RNDN, verbose); test_value (nbtests, 53, MPFR_RNDZ, verbose); test_value (nbtests, 64, MPFR_RNDA, verbose); tests_end_mpfr (); return 0; }