std::uniform_real_distribution always returns infinity

207 Views Asked by At

When I run this code:

double getRandomDouble() {
    static std::mt19937 entropy_ = std::mt19937();
    std::uniform_real_distribution<double> distribution;
    distribution.param(typename decltype(distribution)::param_type(std::numeric_limits<double>::lowest(),
                                                                   std::numeric_limits<double>::max()));
    return distribution(entropy_);
}

It always returns infinity (at least in GCC8.1 & clang 11.0.1. In MSVC 14.16.27023 it asserts)

Here is a working demonstration in GodBolt

I would expect this function to return any random double value, what is happening here?

3

There are 3 best solutions below

1
TrebledJ On BEST ANSWER

The choice of parameters violates the precondition of std::uniform_real_distribution (c.f. §26.6.9.2.2.2).

The preconditions being a ≤ b and b - a ≤ numeric_limits<T>::max(), where a and b are the min and max of the distribution. Using numeric_limits<double>::lowest() and numeric_limits<double>::max() would go against this.

As suggested by Ted, using a = -max()/2.0, b = max()/2.0 would be a better choice.

3
cbuchart On

You have a numeric problem: the distribution is using a probability function: 1 / (b - a), and in this case b - a is out of the range of a double (which means inf, therefore all the numbers are inf). You can fix it by lowering the limits as in the example below.

In addition, you are always using a new random number generator, and, as the seed is the same, it is always generating the same number. You can either make your generator external to the function so it continues generating new numbers, or better, associate a random device.

#include <random>
#include <iostream>

double getRandomDouble() {
    std::random_device rd; // <<
    std::mt19937 entropy_ = std::mt19937(rd()); // <<
    std::uniform_real_distribution<double> distribution;

    using param = typename decltype(distribution)::param_type;
    distribution.param(param(std::numeric_limits<double>::lowest() / 2, 
                             std::numeric_limits<double>::max() / 2));
    return distribution(entropy_);
}

int main()
{
    for (int i = 0; i < 10; ++i) {
        std::cout << "Random number: " << getRandomDouble() << "\n";
    }
    return 0;
}

You can test it here.

2
ALX23z On

The mandated implementation of uniform_real_distribution is basically distribute uniformly in [0,1) then map to [a,b) by multiplying by (b-a) and then adding a.

It is a fine speed-oriented implementation, but when applied to extreme cases it fails. In your case computing b-a results in infinity and subsequently the whole computation fails.