diff options
| author | Richard Kistruck | 2009-01-20 15:59:32 +0000 |
|---|---|---|
| committer | Richard Kistruck | 2009-01-20 15:59:32 +0000 |
| commit | e486078a79edd55a290e4087ee92c114fa301e0d (patch) | |
| tree | 0ba1b8449ce6add9ab322870846f5f36a0f1d14a /mps/code/testlib.c | |
| parent | 4fc1ea19fbf43ab263c604f26870eb935399c44d (diff) | |
| download | emacs-e486078a79edd55a290e4087ee92c114fa301e0d.tar.gz emacs-e486078a79edd55a290e4087ee92c114fa301e0d.zip | |
Mps br.timing: testlib randomize() was wrong (simply discarding the first 0..32000 values is a throughly defective way to seed the generator). correct it to simply use timestamp (modulo m) as the initial seed, and then iterate 10 times.
rnd(): add notes, and create rnd_verify() to confirm implementation is correct.
Copied from Perforce
Change: 167181
ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code/testlib.c')
| -rw-r--r-- | mps/code/testlib.c | 99 |
1 files changed, 87 insertions, 12 deletions
diff --git a/mps/code/testlib.c b/mps/code/testlib.c index b22ed52bd2c..5c92969d2e0 100644 --- a/mps/code/testlib.c +++ b/mps/code/testlib.c | |||
| @@ -36,21 +36,73 @@ struct itimerspec; /* stop complaints from time.h */ | |||
| 36 | * | 36 | * |
| 37 | * I nabbed it from "ML for the Working Programmer", originally from: | 37 | * I nabbed it from "ML for the Working Programmer", originally from: |
| 38 | * Stephen K Park & Keith W Miller (1988). Random number generators: | 38 | * Stephen K Park & Keith W Miller (1988). Random number generators: |
| 39 | * good one are to find. Communications of the ACM, 31:1192-1201. | 39 | * good ones are hard to find. Communications of the ACM, 31:1192-1201. |
| 40 | * | ||
| 41 | * This is a 'full-period' generator: all values from [1..(mod-1)] | ||
| 42 | * are returned once, and then the generator cycles round again. | ||
| 43 | * (The value 0 is not part of the cycle and is never returned). That | ||
| 44 | * means the period = mod-1, ie. 2147483646. | ||
| 45 | * | ||
| 46 | * This "x 16807 mod 2^31-1" generator has been very well studied. | ||
| 47 | * It is not the 'best' (most random) simple generator, but it is | ||
| 48 | * free of all vices we might care about for this application, so | ||
| 49 | * we'll keep it simple and stick with this, thanks. | ||
| 50 | * | ||
| 51 | * There are faster implementations of this generator, summarised | ||
| 52 | * briefly here: | ||
| 53 | * - L. Schrage (1979 & 1983) showed how to do all the calculations | ||
| 54 | * within 32-bit integers. See also Numerical recipes in C. | ||
| 55 | * - David Carta (1990) showed how to also eliminate division. | ||
| 40 | */ | 56 | */ |
| 41 | 57 | ||
| 58 | static unsigned long seed = 1; | ||
| 59 | /* 2^31 - 1 == (1UL<<31) - 1 == 2147483647 */ | ||
| 60 | #define RND_MOD_2_31_1 2147483647 | ||
| 61 | #define RND_MOD_2_31_1_FLOAT 2147483647.0 | ||
| 42 | unsigned long rnd(void) | 62 | unsigned long rnd(void) |
| 43 | { | 63 | { |
| 44 | static unsigned long seed = 1; | ||
| 45 | double s; | 64 | double s; |
| 46 | 65 | ||
| 47 | s = seed; | 66 | s = seed; |
| 48 | s *= 16807.0; | 67 | s *= 16807.0; |
| 49 | s = fmod(s, 2147483647.0); /* 2^31 - 1 */ | 68 | s = fmod(s, RND_MOD_2_31_1_FLOAT); /* 2^31 - 1 */ |
| 50 | seed = (unsigned long)s; | 69 | seed = (unsigned long)s; |
| 51 | return seed; | 70 | return seed; |
| 71 | /* Have you modified this code? Run rnd_verify() please! RHSK */ | ||
| 52 | } | 72 | } |
| 53 | 73 | ||
| 74 | /* rnd_verify -- verify that rnd() returns the correct results */ | ||
| 75 | static void rnd_verify(void) | ||
| 76 | { | ||
| 77 | unsigned long orig_seed = seed; | ||
| 78 | unsigned long i; | ||
| 79 | |||
| 80 | /* This test is from: | ||
| 81 | * Stephen K Park & Keith W Miller (1988). Random number generators: | ||
| 82 | * good ones are hard to find. Communications of the ACM, 31:1192-1201. | ||
| 83 | * Note: The Park-Miller paper uses 1-based indices. | ||
| 84 | */ | ||
| 85 | i = 1; | ||
| 86 | seed = 1; | ||
| 87 | for(i = 2; i < 10001; i += 1) { | ||
| 88 | (void)rnd(); | ||
| 89 | } | ||
| 90 | Insist(i == 10001); | ||
| 91 | Insist(rnd() == 1043618065); | ||
| 92 | |||
| 93 | /* from Robin Whittle's compendium at | ||
| 94 | * http://www.firstpr.com.au/dsp/rand31/ | ||
| 95 | * Observe wrap-around after 'full-period' (which excludes 0). | ||
| 96 | * Note: uses 0-based indices, ie. rnd_0 = 1, rnd_1 = 16807, ... | ||
| 97 | */ | ||
| 98 | i = 2147483645; | ||
| 99 | seed = 1407677000; | ||
| 100 | i += 1; | ||
| 101 | Insist(i == (1UL<<31) - 2 ); /* 'full-period' excludes 0 */ | ||
| 102 | Insist(rnd() == 1); /* wrap-around */ | ||
| 103 | |||
| 104 | seed = orig_seed; | ||
| 105 | } | ||
| 54 | 106 | ||
| 55 | /* rnd_addr -- a random address generator | 107 | /* rnd_addr -- a random address generator |
| 56 | * | 108 | * |
| @@ -75,19 +127,42 @@ mps_addr_t rnd_addr(void) | |||
| 75 | 127 | ||
| 76 | void randomize(int argc, char **argv) | 128 | void randomize(int argc, char **argv) |
| 77 | { | 129 | { |
| 78 | int i, k, n; | 130 | int n; |
| 131 | unsigned long seed0; | ||
| 132 | int i; | ||
| 79 | 133 | ||
| 80 | if (argc > 1) { | 134 | if (argc > 1) { |
| 81 | n = sscanf(argv[1], "%d", &k); | 135 | n = sscanf(argv[1], "%lu", &seed0); |
| 82 | die((n == 1) ? MPS_RES_OK : MPS_RES_FAIL, "randomize"); | 136 | Insist(n == 1); |
| 137 | printf("randomize(): resetting initial seed to: %lu.\n", seed0); | ||
| 138 | rnd_verify(); /* good to verify occasionally: slip it in here */ | ||
| 83 | } else { | 139 | } else { |
| 84 | k = time(NULL) % 32000; | 140 | /* time_t uses an arbitrary encoding, but hopefully the low order */ |
| 85 | printf("Randomizing %d times.\n", k); | 141 | /* 31 bits will have at least one bit changed from run to run. */ |
| 142 | seed0 = 1 + time(NULL) % (RND_MOD_2_31_1 - 1); | ||
| 143 | printf("randomize(): choosing initial seed: %lu.\n", seed0); | ||
| 86 | } | 144 | } |
| 87 | 145 | ||
| 88 | /* Randomize the random number generator a bit. */ | 146 | Insist(seed0 < RND_MOD_2_31_1); /* 2^31 - 1 */ |
| 89 | for (i = k; i > 0; --i) | 147 | Insist(seed0 != 0); |
| 90 | rnd(); | 148 | seed = seed0; |
| 149 | |||
| 150 | /* The 'random' seed is taken from time(), which may simply be a | ||
| 151 | * count of seconds: therefore successive runs may start with | ||
| 152 | * nearby seeds, possibly differing only by 1. So the first value | ||
| 153 | * returned by rnd() may differ by only 16807. It is conceivable | ||
| 154 | * that some tests might be able to 'spot' this pattern (for | ||
| 155 | * example: by using the first rnd() value, mod 100M and rounded | ||
| 156 | * to multiple of 128K, as arena size). | ||
| 157 | * | ||
| 158 | * So to mix it up a bit, we do a few iterations now. How many? | ||
| 159 | * Very roughly, 16807^2 is of the same order as 2^31, so two | ||
| 160 | * iterations would make the characteristic difference similar to | ||
| 161 | * the period. Hey, let's go wild and do 10. | ||
| 162 | */ | ||
| 163 | for(i = 0; i < 10; i += 1) { | ||
| 164 | (void)rnd(); | ||
| 165 | } | ||
| 91 | } | 166 | } |
| 92 | 167 | ||
| 93 | 168 | ||
| @@ -146,7 +221,7 @@ void cdie(int res, const char *s) | |||
| 146 | 221 | ||
| 147 | /* C. COPYRIGHT AND LICENSE | 222 | /* C. COPYRIGHT AND LICENSE |
| 148 | * | 223 | * |
| 149 | * Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>. | 224 | * Copyright (C) 2001-2002, 2008 Ravenbrook Limited <http://www.ravenbrook.com/>. |
| 150 | * All rights reserved. This is an open source license. Contact | 225 | * All rights reserved. This is an open source license. Contact |
| 151 | * Ravenbrook for commercial licensing options. | 226 | * Ravenbrook for commercial licensing options. |
| 152 | * | 227 | * |