简体   繁体   中英

std::uniform_real_distribution always returns infinity

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?

The choice of parameters violates the precondition of std::uniform_real_distribution (cf §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.

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

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

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM