I noticed a very weird behavior with GNU C++ standard library.
When using a custom generator with normal distribution, it seems the distribution might return different values even for the same generator result.
Example code
#include <iostream>
#include <string>
#include <random>
template <int MAX>
class Generator
{
public:
long long operator()() {return (++state) % MAX;}
long long min() {return 0;}
long long max() {return MAX;}
long long state = 0;
};
int main()
{
// 3 identical generators, 2 identical distributions
Generator<5> gen0, gen1, gen2;
std::normal_distribution<float> dist1(10, 1), dist2(10,1);
// Print: iteration, generator output, distributions output
printf("i gen dist1 dist2\n");
for (int i = 0; i < 10; i++) {
printf("%d %lld %10.6f %10.6f\n", i, gen0(), dist1(gen1), dist2(gen2));
dist2.reset();
}
}
This produces (g++ 6.2.1, same on newer ones):
i gen dist1 dist2
0 1 11.295982 11.295982
1 2 9.325577 10.256990
2 3 10.256990 9.672720
3 4 8.679697 9.300377
4 0 9.672720 8.957100
5 1 9.097454 11.295982
6 2 9.300377 10.256990
7 3 10.582899 9.672720
8 4 8.957100 9.300377
9 0 10.169774 8.957100
See how the dist1
results in rows 0 and 5 are different, even though generator produces the same value 1
. For dist2
, they're the same, as we call reset()
.
Btw, the reason seems to be that the GNU C++ library generates values in pairs, and caches them between the calls, not necessarily calling the generator. This also explains why dist1 row 4 is the same as dist2 (which resets the distribution) row 2.
I searched in the documentation, but I couldn't figure out if this behavior is standard compliant. Hence, I don't know if it's a standard library bug :)
Note, from what I saw, std::normal_distribution is the only class exhibiting this behavior.
It's standard compliant. There is no requirement that the generator be stateless. If there were, the reset
function would serve no purpose.