std::uniform_real_distribution 总是 returns 无限

std::uniform_real_distribution always returns infinity

当我运行这段代码时:

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_);
}

它总是return无穷大(至少在 GCC8.1 和 clang 11.0.1 中是这样。在 MSVC 14.16.27023 中它断言)

Here is a working demonstration in GodBolt

我希望此函数 return 任何随机双精度值,这里发生了什么?

你有一个数字问题:分布使用概率函数:1 / (b - a),在这种情况下 b - a 超出了 double 的范围(这意味着 inf,所以所有的数字都是inf)。您可以通过降低限制来修复它,如下例所示。

此外,您一直在使用新的随机数生成器,并且由于种子相同,所以生成的随机数始终相同。您可以将生成器置于函数外部,以便它继续生成新数字,或者更好地关联一个随机设备。

#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;
}

你可以测试一下here

uniform_real_distribution的强制执行基本上是在[0,1)中统一分配然后映射到[a,b)乘以(b-a)然后加上a

这是一个很好的面向速度的实现,但是当应用于极端情况时它会失败。在您的情况下,计算 b-a 会导致无穷大,随后整个计算都会失败。

参数的选择违反了std::uniform_real_distribution的前提条件(c.f。§26.6.9.2.2.2)。

先决条件是 a ≤ bb - a ≤ numeric_limits<T>::max(),其中 ab 是分布的最小值和最大值。使用 numeric_limits<double>::lowest()numeric_limits<double>::max() 会违背这一点。

根据的建议,使用a = -max()/2.0b = max()/2.0会是更好的选择。