]> git.draconx.ca Git - cdecl99.git/commitdiff
tests: Use 32-bit arithmetic for seed expansion.
authorNick Bowler <nbowler@draconx.ca>
Fri, 23 Feb 2024 03:26:01 +0000 (22:26 -0500)
committerNick Bowler <nbowler@draconx.ca>
Fri, 23 Feb 2024 03:26:01 +0000 (22:26 -0500)
To facilitate a future port to older C implementations, replace
splitmix64 in the seed expansion with a very similar algorithm
based purely on 32-bit arithmetic.

This changes the generated random sequences for a particular seed
but means there is no need to implement a 64-bit multiplier for
such a trivial purpose.

t/rng-test.c
t/rng.c

index e4173aac28898df37b5106836bbd67c70770edda..c70f752dc1eee0971defddc9aa5691fa4fecf545 100644 (file)
@@ -33,17 +33,17 @@ int main(void)
 
 int main(void)
 {
-       unsigned long long seed_state = 0xdeadbeeff00dcafe;
        unsigned long long test_result, ref_result;
        unsigned long long ref_state[4], test_state[4];
+       uint_least32_t seed_state = 0xdeadbeef;
        int i, ret = 0;
 
        tap_plan(200);
        for (i = 0; i < 100; i++) {
-               s[0] = ref_state[0] = test_state[0] = splitmix64(&seed_state);
-               s[1] = ref_state[1] = test_state[1] = splitmix64(&seed_state);
-               s[2] = ref_state[2] = test_state[2] = splitmix64(&seed_state);
-               s[3] = ref_state[3] = test_state[3] = splitmix64(&seed_state);
+               s[0] = ref_state[0] = test_state[0] = seed64(&seed_state);
+               s[1] = ref_state[1] = test_state[1] = seed64(&seed_state);
+               s[2] = ref_state[2] = test_state[2] = seed64(&seed_state);
+               s[3] = ref_state[3] = test_state[3] = seed64(&seed_state);
 
                ref_result = next();
                test_result = xoshiro256p(test_state);
diff --git a/t/rng.c b/t/rng.c
index a827b2e05f610255de5368784750b6b5a737b787..63e08188291b1972eec472819e3d46d655883414 100644 (file)
--- a/t/rng.c
+++ b/t/rng.c
@@ -15,9 +15,9 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  *
- * The RNG implementation is adapted from xoshiro256+ and splitmix64
- * by David Blackman and Sebastiano Vigna, originally distributed under
- * the Creative Commons Zero public domain dedication.
+ * The RNG implementation is adapted from xoshiro256+ by David Blackman
+ * and Sebastiano Vigna, originally distributed under the Creative Commons
+ * Zero public domain dedication.
  */
 
 #include <config.h>
 #include <string.h>
 #include <errno.h>
 #include <limits.h>
+#include <inttypes.h>
 
 #if !TEST_RNG_NO_EXTERNAL_API
 #  include "test.h"
 #endif
 
 #define B64(x) ((x) & 0xffffffffffffffffull)
+#define B32(x) ((x) & 0xffffffffu)
 
 struct test_rng {
        unsigned long long state[4];
@@ -60,38 +62,51 @@ static unsigned long long xoshiro256p(unsigned long long *s)
        return ret;
 }
 
-static unsigned long long splitmix64(unsigned long long *state)
+/*
+ * 32-bit random number generator for seed expansion.
+ *
+ * The output filter is from the public domain MurmurHash3 by Austin Appleby.
+ *
+ * The state is incremented by the fractional part of the golden ratio,
+ * i.e., floor( (sqrt(5)-1)/2 * 2**32 )
+ */
+static uint_least32_t splitmix32(uint_least32_t *state)
 {
-       unsigned long long z;
+       uint_least32_t z;
 
-       z = B64(*state += 0x9e3779b97f4a7c15ull);
-       z = B64((z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ull);
-       z = B64((z ^ (z >> 27)) * 0x94d049bb133111ebull);
+       z = B32(*state += 0x9e3779b9);
+       z = B32((z ^ (z >> 16)) * 0x85ebca6b);
+       z = B32((z ^ (z >> 13)) * 0xc2b2ae35);
 
-       return z ^ (z >> 31);
+       return z ^ (z >> 16);
+}
+
+static uint_least64_t seed64(uint_least32_t *state)
+{
+       uint_least64_t x;
+
+       x = splitmix32(state);
+       return (x << 32) | splitmix32(state);
 }
 
 #if !TEST_RNG_NO_EXTERNAL_API
 struct test_rng *test_rng_alloc(const char *seed_str)
 {
-       test_uintmax limit, seed_val;
-       unsigned long long seed;
+       test_uintmax seed_val;
        struct test_rng *rng;
+       uint_least32_t seed;
 
-       limit  = 0xffffffff;
-       limit |= (limit << 16 << 16);
-
-       if (!test_strtoumax(&seed_val, seed_str, limit)) {
+       if (!test_strtoumax(&seed_val, seed_str, 0xffffffff)) {
                print_error("%s: invalid seed", seed_str);
                return NULL;
        }
        seed = seed_val;
 
        rng = malloc_nofail(sizeof *rng);
-       rng->state[0] = splitmix64(&seed);
-       rng->state[1] = splitmix64(&seed);
-       rng->state[2] = splitmix64(&seed);
-       rng->state[3] = splitmix64(&seed);
+       rng->state[0] = seed64(&seed);
+       rng->state[1] = seed64(&seed);
+       rng->state[2] = seed64(&seed);
+       rng->state[3] = seed64(&seed);
 
        return rng;
 }