8

I am using GCC 4.6.3 and was trying to generate random numbers with the following code:

#include <random>
#include <functional>

int main()
{
    std::mt19937 rng_engine;

    printf("With bind\n");
    for(int i = 0; i < 5; ++i) {
        std::uniform_real_distribution<double> dist(0.0, 1.0);
        auto rng = std::bind(dist, rng_engine);
        printf("%g\n", rng());
    }

    printf("Without bind\n");
    for(int i = 0; i < 5; ++i) {
        std::uniform_real_distribution<double> dist(0.0, 1.0);
        printf("%g\n", dist(rng_engine));
    }

    return 0;
}

I expected both methods to generate a sequence of 5 random numbers. Instead, this is what I actually get:

With bind
0.135477
0.135477
0.135477
0.135477
0.135477
Without bind
0.135477
0.835009
0.968868
0.221034
0.308167

Is this a GCC bug? Or is it some subtle issue having to do with std::bind? If so, can you make any sense of the result?

Thanks.

patvarilly
  • 105
  • 1
  • 5

3 Answers3

14

When binding, a copy of rng_engine is made. If you want to pass a reference, this is what you have to do :

auto rng = std::bind(dist, std::ref(rng_engine));
Tristram Gräbener
  • 9,601
  • 3
  • 34
  • 50
  • 2
    Not fair, I had a call from mom before posting +1 for speed. – nurettin Dec 24 '12 at 17:02
  • 2
    Damn, I cannot just answer with a smiley :) – Tristram Gräbener Dec 24 '12 at 17:03
  • Slightly off topic, but what if you wanna use bind and pass a generator by reference to each std::thread? – pyCthon Dec 24 '12 at 17:10
  • 1
    If you pass the generator by reference to different threads, you might get in trouble : as the generator has an internal state you will have concurrency problems. Therefore mutex and headaches. It would be safer to give each thread its own generator (with different seeds) – Tristram Gräbener Dec 24 '12 at 17:14
  • Ahh! Of course, makes sense. I had seen this pattern from Wikipedia (http://en.wikipedia.org/wiki/C%2B%2B11) and cplusplus.com (http://www.cplusplus.com/reference/random/). Might be worthwhile to correct it there as well if anyone knows how. Thanks! – patvarilly Dec 24 '12 at 18:06
5

The std::uniform_real_distribution::operator() takes a Generator & so you will have to bind using std::ref

#include <random>
#include <functional>

int main()
{
    std::mt19937 rng_engine;

    printf("With bind\n");
    for(int i = 0; i < 5; ++i) {
        std::uniform_real_distribution<double> dist(0.0, 1.0);
        auto rng = std::bind(dist, std::ref(rng_engine));
        printf("%g\n", rng());
    }

    printf("Without bind\n");
    for(int i = 0; i < 5; ++i) {
        std::uniform_real_distribution<double> dist(0.0, 1.0);
        printf("%g\n", dist(rng_engine));
    }
}
nurettin
  • 11,090
  • 5
  • 65
  • 85
3

bind() is for repeated uses.

Putting it outside of the loop...

std::mt19937 rng_engine;
std::uniform_real_distribution<double> dist(0.0, 1.0);
auto rng = std::bind(dist, rng_engine);

for(int i = 0; i < 5; ++i) {
    printf("%g\n", rng());
}

... gives me the expected result:

0.135477
0.835009
0.968868
0.221034
0.308167
a.l.e
  • 792
  • 8
  • 16