├── README.md ├── splitmix64.h ├── test-vector.c ├── test-vector.cpp ├── test-vector.py ├── test-vector.txt └── xoshiro256ss.h /README.md: -------------------------------------------------------------------------------- 1 | A minimal xoshiro256** generator, with the C++ interface. 2 | 3 | Generate 64-bit random integers, speedily, with code small enough to 4 | paste directly into your project. 5 | 6 | By Arthur O'Dwyer, based on code by David Blackman and Sebastiano Vigna. 7 | All errors are my own. 8 | Public domain. 9 | 10 | * https://prng.di.unimi.it/xoshiro256starstar.c 11 | 12 | * https://prng.di.unimi.it/splitmix64.c 13 | 14 | ## Test vectors 15 | 16 | Initialize `xoshiro256**` with the SplitMix64 of seed `100`, then 17 | generate four 64-bit results; they should be 18 | 19 | 792317387143481937 20 | 1418856489092323125 21 | 6662743737787356053 22 | 9823178768685107703 23 | 24 | or, in hex, 25 | 26 | 0afee077'3a0d8a51 27 | 13b0ca75'9b9b1735 28 | 5c76d220'f8461395 29 | 8852f10b'70a289f7 30 | 31 | In this repository, `test-vector.cpp` uses this C++ `xoshiro256ss.h` implementation; 32 | `test-vector.c` uses Blackman and Vigna's C code; and `test-vector.py` uses the 33 | implementation from the Python `randomgen` package. All three produce the expected 34 | output on my machine. 35 | -------------------------------------------------------------------------------- /splitmix64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The "splitmix64" generator. 4 | // C++ port by Arthur O'Dwyer (2021). 5 | // Based on the C version by Sebastiano Vigna (2015), 6 | // https://prng.di.unimi.it/splitmix64.c 7 | 8 | static_assert(sizeof(long long) == 8, "64-bit machines only"); 9 | 10 | struct splitmix64 { 11 | using u64 = unsigned long long; 12 | u64 x; 13 | 14 | constexpr explicit splitmix64(u64 seed) : x(seed) {} 15 | 16 | using result_type = u64; 17 | static constexpr u64 min() { return 0; } 18 | static constexpr u64 max() { return u64(-1); } 19 | 20 | constexpr u64 operator()() { 21 | u64 z = (x += 0x9e3779b97f4a7c15uLL); 22 | z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9uLL; 23 | z = (z ^ (z >> 27)) * 0x94d049bb133111ebuLL; 24 | return z ^ (z >> 31); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /test-vector.c: -------------------------------------------------------------------------------- 1 | /* Written in 2015 by Sebastiano Vigna (vigna@acm.org) 2 | 3 | To the extent possible under law, the author has dedicated all copyright 4 | and related and neighboring rights to this software to the public domain 5 | worldwide. This software is distributed without any warranty. 6 | 7 | See . */ 8 | 9 | #include 10 | 11 | /* This is a fixed-increment version of Java 8's SplittableRandom generator 12 | See http://dx.doi.org/10.1145/2714064.2660195 and 13 | http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html 14 | 15 | It is a very fast generator passing BigCrush, and it can be useful if 16 | for some reason you absolutely want 64 bits of state. */ 17 | 18 | static uint64_t x; /* The state can be seeded with any value. */ 19 | 20 | uint64_t splitmix_next() { 21 | uint64_t z = (x += 0x9e3779b97f4a7c15); 22 | z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; 23 | z = (z ^ (z >> 27)) * 0x94d049bb133111eb; 24 | return z ^ (z >> 31); 25 | } 26 | 27 | /* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) 28 | 29 | To the extent possible under law, the author has dedicated all copyright 30 | and related and neighboring rights to this software to the public domain 31 | worldwide. This software is distributed without any warranty. 32 | 33 | See . */ 34 | 35 | #include 36 | 37 | /* This is xoshiro256** 1.0, one of our all-purpose, rock-solid 38 | generators. It has excellent (sub-ns) speed, a state (256 bits) that is 39 | large enough for any parallel application, and it passes all tests we 40 | are aware of. 41 | 42 | For generating just floating-point numbers, xoshiro256+ is even faster. 43 | 44 | The state must be seeded so that it is not everywhere zero. If you have 45 | a 64-bit seed, we suggest to seed a splitmix64 generator and use its 46 | output to fill s. */ 47 | 48 | static inline uint64_t rotl(const uint64_t x, int k) { 49 | return (x << k) | (x >> (64 - k)); 50 | } 51 | 52 | 53 | static uint64_t s[4]; 54 | 55 | uint64_t next(void) { 56 | const uint64_t result = rotl(s[1] * 5, 7) * 9; 57 | 58 | const uint64_t t = s[1] << 17; 59 | 60 | s[2] ^= s[0]; 61 | s[3] ^= s[1]; 62 | s[1] ^= s[2]; 63 | s[0] ^= s[3]; 64 | 65 | s[2] ^= t; 66 | 67 | s[3] = rotl(s[3], 45); 68 | 69 | return result; 70 | } 71 | 72 | 73 | /* This is the jump function for the generator. It is equivalent 74 | to 2^128 calls to next(); it can be used to generate 2^128 75 | non-overlapping subsequences for parallel computations. */ 76 | 77 | void jump(void) { 78 | static const uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; 79 | 80 | uint64_t s0 = 0; 81 | uint64_t s1 = 0; 82 | uint64_t s2 = 0; 83 | uint64_t s3 = 0; 84 | for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) 85 | for(int b = 0; b < 64; b++) { 86 | if (JUMP[i] & UINT64_C(1) << b) { 87 | s0 ^= s[0]; 88 | s1 ^= s[1]; 89 | s2 ^= s[2]; 90 | s3 ^= s[3]; 91 | } 92 | next(); 93 | } 94 | 95 | s[0] = s0; 96 | s[1] = s1; 97 | s[2] = s2; 98 | s[3] = s3; 99 | } 100 | 101 | 102 | 103 | /* This is the long-jump function for the generator. It is equivalent to 104 | 2^192 calls to next(); it can be used to generate 2^64 starting points, 105 | from each of which jump() will generate 2^64 non-overlapping 106 | subsequences for parallel distributed computations. */ 107 | 108 | void long_jump(void) { 109 | static const uint64_t LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 }; 110 | 111 | uint64_t s0 = 0; 112 | uint64_t s1 = 0; 113 | uint64_t s2 = 0; 114 | uint64_t s3 = 0; 115 | for(int i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++) 116 | for(int b = 0; b < 64; b++) { 117 | if (LONG_JUMP[i] & UINT64_C(1) << b) { 118 | s0 ^= s[0]; 119 | s1 ^= s[1]; 120 | s2 ^= s[2]; 121 | s3 ^= s[3]; 122 | } 123 | next(); 124 | } 125 | 126 | s[0] = s0; 127 | s[1] = s1; 128 | s[2] = s2; 129 | s[3] = s3; 130 | } 131 | 132 | #include 133 | 134 | int main() { 135 | x = 100; 136 | s[0] = splitmix_next(); 137 | s[1] = splitmix_next(); 138 | s[2] = splitmix_next(); 139 | s[3] = splitmix_next(); 140 | printf("%llu\n", next()); 141 | printf("%llu\n", next()); 142 | printf("%llu\n", next()); 143 | printf("%llu\n", next()); 144 | } 145 | -------------------------------------------------------------------------------- /test-vector.cpp: -------------------------------------------------------------------------------- 1 | #include "xoshiro256ss.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | constexpr bool test() { 8 | xoshiro256ss x(100); 9 | assert(x() == 792317387143481937uLL); 10 | assert(x() == 1418856489092323125uLL); 11 | assert(x() == 6662743737787356053uLL); 12 | assert(x() == 9823178768685107703uLL); 13 | return true; 14 | } 15 | static_assert(test(), "compile-time OK"); 16 | 17 | void shuffle_example() { 18 | int cards[52] {}; 19 | xoshiro256ss g; 20 | std::shuffle(cards, cards + 52, g); 21 | (void) std::uniform_int_distribution(1,6)(g); 22 | (void) std::uniform_real_distribution(1,6)(g); 23 | } 24 | 25 | int main() { 26 | xoshiro256ss x(100); 27 | std::cout << x() << '\n'; 28 | std::cout << x() << '\n'; 29 | std::cout << x() << '\n'; 30 | std::cout << x() << '\n'; 31 | 32 | test(); 33 | shuffle_example(); 34 | } 35 | -------------------------------------------------------------------------------- /test-vector.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import randomgen 4 | 5 | # https://bashtage.github.io/randomgen/bit_generators/xoshiro256.html 6 | 7 | for i in randomgen.xoshiro256.Xoshiro256(100, mode="legacy").random_raw(4): 8 | print(i) 9 | -------------------------------------------------------------------------------- /test-vector.txt: -------------------------------------------------------------------------------- 1 | 792317387143481937 2 | 1418856489092323125 3 | 6662743737787356053 4 | 9823178768685107703 5 | -------------------------------------------------------------------------------- /xoshiro256ss.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The "xoshiro256** 1.0" generator. 4 | // C++ port by Arthur O'Dwyer (2021). 5 | // Based on the C version by David Blackman and Sebastiano Vigna (2018), 6 | // https://prng.di.unimi.it/xoshiro256starstar.c 7 | 8 | static_assert(sizeof(long long) == 8, "64-bit machines only"); 9 | 10 | struct xoshiro256ss { 11 | using u64 = unsigned long long; 12 | u64 s[4] {}; 13 | 14 | static constexpr u64 splitmix64(u64& x) { 15 | u64 z = (x += 0x9e3779b97f4a7c15uLL); 16 | z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9uLL; 17 | z = (z ^ (z >> 27)) * 0x94d049bb133111ebuLL; 18 | return z ^ (z >> 31); 19 | } 20 | 21 | constexpr explicit xoshiro256ss() : xoshiro256ss(0) {} 22 | 23 | constexpr explicit xoshiro256ss(u64 seed) { 24 | s[0] = splitmix64(seed); 25 | s[1] = splitmix64(seed); 26 | s[2] = splitmix64(seed); 27 | s[3] = splitmix64(seed); 28 | } 29 | 30 | using result_type = u64; 31 | static constexpr u64 min() { return 0; } 32 | static constexpr u64 max() { return u64(-1); } 33 | 34 | static constexpr u64 rotl(u64 x, int k) { 35 | return (x << k) | (x >> (64 - k)); 36 | } 37 | 38 | constexpr u64 operator()() { 39 | u64 result = rotl(s[1] * 5, 7) * 9; 40 | u64 t = s[1] << 17; 41 | s[2] ^= s[0]; 42 | s[3] ^= s[1]; 43 | s[1] ^= s[2]; 44 | s[0] ^= s[3]; 45 | s[2] ^= t; 46 | s[3] = rotl(s[3], 45); 47 | return result; 48 | } 49 | }; 50 | --------------------------------------------------------------------------------