tl;dr: Do not use <random> 's generators. There are plenty good alternatives, like PCG, SplitMix, truncated Xorshift32*, or Xorshift1024*.

A recent tweet reminded me of the <random> facilities in the C++ standard library. Having just recently studied and implemented a couple random number generators myself, I was curious how they hold up in a modern test.

From the get-go, I knew this is going to end badly, but I gave it a shot anyway.

I build a little test program, to feed into PractRand:

#include <random> #include <stdio.h> #include <stdint.h> #include <stddef.h> template<class Rand, class T> static void run() { std::random_device rd; Rand gen(rd()); std::uniform_int_distribution<T> dis(0); T val; while (1) { val = dis(gen); fwrite((void *)&val, 1, sizeof(T), stdout); } } int main() { freopen(nullptr, "wb", stdout); /* only required for windows */ run<std::ranlux24_base, uint32_t>(); return 0; }

The test program is printing unsigned 32bit numbers to stdout, which will be piped into the RNG_Test executable from the PractRand suite.

I compiled a single executable for each generator, and started the tests.

Results

Most of the tests were over in just a couple seconds.

generator 256M 512M 1G 2G 4G 8G 16G 32G 64G 128G 256G 512G 1T ranlux24_base ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ranlux48_base ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ minstd_rand ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ minstd_rand0 ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ knuth_b ✓ ✓ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ mt19937 ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✗ ✗ ✗ mt19937_64 ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✗ ✗ ranlux24 ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ranlux48 ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓

The table shows after how many iterations the generator fails the statistical tests.

The earlier it fails, the worse it is. Modern PRNGs, like PCG, are known to not fail the statistical tests at all.

That's only have the story, though.

With the table above showing the statistical quality of each generator, the following is listing it's properties:

Name Predictability Performance State Period Mersenne Twister Trivial Ok 2 KiB 2^19937 LCG (minstd) Trivial Ok 4-8 byte <= 2^32 LFG (ranluxXX_base) Trivial Slow 8-16 byte <= 2^32 Knuth Trivial Ok 1KiB <= 2^32 ranlux Unknown? Super Slow ~120 byte 10^171

While both the 32-bit and 64-bit mersenne twister fail ( mt19937 & mt19937_64 ) at some seemingly high amount of data (256G and 512G, respectively), they are fundamentally flawed. They're easily predictable, large (2.5KiB of state) and multiple times slower than any other alternative.

If you must use any generator from <random> , use either ranlux24 or ranlux48 , the only two which showed a reasonable result in the test. At the expense of performance.

While <random> has the building blocks to build a decent generator, with good statistical quality, it requires knowledge of the domain, and is not trivial. The predefined generators didn't stand the test of time, and are weak throughout. To make matters worse, the default_random_engine often defaults to mt19937 , which, while not as bad as the other choices, is neither fast, lean nor unpredictable.

There is a certain irony, that it's known that rand() is terrible generator, while everybody is fine that C++ includes almost the exact same generator ( rand() is usually implemented as an LCG).

Raw Test Results

Following are the raw tests results linked, for your viewing pleasure.

ranlux24_base

$ ./ranlux24_base | ./PractRand/RNG_test stdin32 -multithreaded RNG_test using PractRand version 0.93 RNG = RNG_stdin32, seed = 0xc6851f49 test set = normal, folding = standard (32 bit) rng=RNG_stdin32, seed=0xc6851f49 length= 256 megabytes (2^28 bytes), time= 3.5 seconds Test Name Raw Processed Evaluation BCFN(2+1,13-2,T) R= +9.7 p = 1.5e-4 mildly suspicious BCFN(2+2,13-3,T) R= +9.0 p = 4.6e-4 unusual [Low8/32]BCFN(2+0,13-3,T) R= +91.1 p = 6.3e-43 FAIL !!! [Low8/32]BCFN(2+1,13-3,T) R= +24.4 p = 2.4e-11 FAIL [Low8/32]BCFN(2+2,13-4,T) R= +12.3 p = 2.4e-5 mildly suspicious [Low1/32]BCFN(2+0,13-5,T) R= +45.5 p = 5.4e-18 FAIL ! [Low1/32]DC6-9x1Bytes-1 R=+485.8 p = 1.8e-256 FAIL !!!!!! ...and 117 test result(s) without anomalies

ranlux48_base

$ ./ranlux48_base | ./PractRand/RNG_test stdin32 -multithreaded RNG_test using PractRand version 0.93 RNG = RNG_stdin32, seed = 0x13294a68 test set = normal, folding = standard (32 bit) rng=RNG_stdin32, seed=0x13294a68 length= 256 megabytes (2^28 bytes), time= 2.8 seconds Test Name Raw Processed Evaluation BCFN(2+1,13-2,T) R= +28.1 p = 6.0e-14 FAIL [Low8/32]BCFN(2+0,13-3,T) R=+251.2 p = 1.0e-118 FAIL !!!!! [Low8/32]BCFN(2+1,13-3,T) R= +20.5 p = 1.6e-9 VERY SUSPICIOUS [Low1/32]BCFN(2+0,13-5,T) R= +16.0 p = 1.9e-6 very suspicious [Low1/32]DC6-9x1Bytes-1 R= +49.8 p = 3.4e-26 FAIL !! ...and 119 test result(s) without anomalies

minstd_rand

$ ./minstd_rand | ./PractRand/RNG_Test stdin32 -multithreaded RNG_test using PractRand version 0.93 RNG = RNG_stdin32, seed = 0x665a9f8e test set = normal, folding = standard (32 bit) rng=RNG_stdin32, seed=0x665a9f8e length= 256 megabytes (2^28 bytes), time= 3.9 seconds Test Name Raw Processed Evaluation FPF-14+6/16:(0,14-0) R= -7.9 p =1-6.2e-7 mildly suspicious FPF-14+6/16:(1,14-0) R= -7.1 p =1-2.8e-6 mildly suspicious FPF-14+6/16:(2,14-0) R= -6.3 p =1-1.8e-5 unusual FPF-14+6/16:all R= -13.4 p =1-1.0e-12 FAIL FPF-14+6/16:all2 R= +24.9 p = 2.8e-11 VERY SUSPICIOUS [Low8/32]FPF-14+6/16:all R= -6.7 p =1-4.2e-6 suspicious ...and 118 test result(s) without anomalies

minstd_rand0

$ ./minstd_rand0 | ./PractRand/RNG_Test stdin32 -multithreaded RNG_test using PractRand version 0.93 RNG = RNG_stdin32, seed = 0x3b74f60f test set = normal, folding = standard (32 bit) rng=RNG_stdin32, seed=0x3b74f60f length= 128 megabytes (2^27 bytes), time= 2.2 seconds Test Name Raw Processed Evaluation BCFN(2+2,13-3,T) R= +7.8 p = 1.8e-3 unusual FPF-14+6/16:(0,14-0) R= -6.5 p =1-1.0e-5 unusual FPF-14+6/16:all R= -8.2 p =1-1.5e-7 very suspicious FPF-14+6/16:all2 R= +10.6 p = 1.3e-5 mildly suspicious ...and 113 test result(s) without anomalies rng=RNG_stdin32, seed=0x3b74f60f length= 256 megabytes (2^28 bytes), time= 4.6 seconds Test Name Raw Processed Evaluation FPF-14+6/16:(0,14-0) R= -9.4 p =1-2.6e-8 very suspicious FPF-14+6/16:(2,14-0) R= -8.7 p =1-9.5e-8 suspicious FPF-14+6/16:all R= -13.5 p =1-8.8e-13 FAIL FPF-14+6/16:all2 R= +31.8 p = 5.2e-14 FAIL [Low8/32]FPF-14+6/16:all R= -4.2 p =1-1.2e-3 unusual [Low1/32]BCFN(2+1,13-5,T) R= -6.5 p =1-5.1e-4 unusual ...and 118 test result(s) without anomalies

knuth_b

$ ./knuth_b | ./PractRand/RNG_Test stdin32 -multithreaded RNG_test using PractRand version 0.93 RNG = RNG_stdin32, seed = 0x2ba764e1 test set = normal, folding = standard (32 bit) rng=RNG_stdin32, seed=0x2ba764e1 length= 128 megabytes (2^27 bytes), time= 2.9 seconds no anomalies in 117 test result(s) rng=RNG_stdin32, seed=0x2ba764e1 length= 256 megabytes (2^28 bytes), time= 5.8 seconds Test Name Raw Processed Evaluation FPF-14+6/16:all R= -5.5 p =1-6.4e-5 mildly suspicious ...and 123 test result(s) without anomalies rng=RNG_stdin32, seed=0x2ba764e1 length= 512 megabytes (2^29 bytes), time= 10.9 seconds Test Name Raw Processed Evaluation FPF-14+6/16:(0,14-0) R= -6.8 p =1-5.5e-6 unusual FPF-14+6/16:all R= -8.7 p =1-4.7e-8 very suspicious FPF-14+6/16:all2 R= +8.9 p = 4.2e-5 mildly suspicious ...and 129 test result(s) without anomalies rng=RNG_stdin32, seed=0x2ba764e1 length= 1 gigabyte (2^30 bytes), time= 20.8 seconds Test Name Raw Processed Evaluation FPF-14+6/16:(0,14-0) R= -12.8 p =1-1.9e-11 VERY SUSPICIOUS FPF-14+6/16:(2,14-0) R= -6.7 p =1-7.3e-6 unusual FPF-14+6/16:all R= -15.4 p =1-1.1e-14 FAIL ! FPF-14+6/16:all2 R= +44.4 p = 2.4e-19 FAIL ! ...and 137 test result(s) without anomalies

mt19937

$ ./mt19937 | ./PractRand/RNG_test stdin32 -multithreaded RNG_test using PractRand version 0.93 RNG = RNG_stdin32, seed = 0xadda9d07 test set = normal, folding = standard (32 bit) rng=RNG_stdin32, seed=0xadda9d07 length= 512 megabytes (2^29 bytes), time= 3.5 seconds no anomalies in 132 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 1 gigabyte (2^30 bytes), time= 7.0 seconds no anomalies in 141 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 2 gigabytes (2^31 bytes), time= 13.6 seconds no anomalies in 148 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 4 gigabytes (2^32 bytes), time= 26.5 seconds no anomalies in 156 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 8 gigabytes (2^33 bytes), time= 52.4 seconds no anomalies in 165 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 16 gigabytes (2^34 bytes), time= 104 seconds no anomalies in 172 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 32 gigabytes (2^35 bytes), time= 203 seconds no anomalies in 180 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 64 gigabytes (2^36 bytes), time= 404 seconds no anomalies in 189 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 128 gigabytes (2^37 bytes), time= 794 seconds no anomalies in 196 test result(s) rng=RNG_stdin32, seed=0xadda9d07 length= 256 gigabytes (2^38 bytes), time= 1620 seconds Test Name Raw Processed Evaluation DC6-9x1Bytes-1 R= -5.3 p =1-3.6e-3 unusual [Low8/32]BRank(12):12K(1) R= +6116 p~= 4e-1842 FAIL !!!!!!!! ...and 202 test result(s) without anomalies

ranlux24

$ ./ranlux24 | ./PractRand/RNG_test stdin32 -multithreaded RNG_test using PractRand version 0.93 RNG = RNG_stdin32, seed = 0xd8322a9c test set = normal, folding = standard (32 bit) rng=RNG_stdin32, seed=0xd8322a9c length= 64 megabytes (2^26 bytes), time= 2.1 seconds no anomalies in 108 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 128 megabytes (2^27 bytes), time= 4.6 seconds no anomalies in 117 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 256 megabytes (2^28 bytes), time= 9.2 seconds no anomalies in 124 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 512 megabytes (2^29 bytes), time= 17.6 seconds no anomalies in 132 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 1 gigabyte (2^30 bytes), time= 33.7 seconds no anomalies in 141 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 2 gigabytes (2^31 bytes), time= 65.8 seconds no anomalies in 148 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 4 gigabytes (2^32 bytes), time= 129 seconds no anomalies in 156 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 8 gigabytes (2^33 bytes), time= 256 seconds no anomalies in 165 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 16 gigabytes (2^34 bytes), time= 510 seconds no anomalies in 172 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 32 gigabytes (2^35 bytes), time= 1008 seconds no anomalies in 180 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 64 gigabytes (2^36 bytes), time= 1920 seconds no anomalies in 189 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 128 gigabytes (2^37 bytes), time= 3743 seconds no anomalies in 196 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 256 gigabytes (2^38 bytes), time= 7372 seconds no anomalies in 204 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 512 gigabytes (2^39 bytes), time= 14681 seconds Test Name Raw Processed Evaluation [Low1/32]DC6-9x1Bytes-1 R= +4.5 p = 0.010 unusual ...and 212 test result(s) without anomalies rng=RNG_stdin32, seed=0xd8322a9c length= 1 terabyte (2^40 bytes), time= 29803 seconds no anomalies in 220 test result(s) rng=RNG_stdin32, seed=0xd8322a9c length= 2 terabytes (2^41 bytes), time= 60168 seconds Test Name Raw Processed Evaluation [Low1/32]DC6-9x1Bytes-1 R= +5.6 p = 8.4e-3 unusual ...and 227 test result(s) without anomalies

ranlux48