在-DBL_MAX和DBL_MAX之间分配的随机双精度数

时间:2017-02-10 16:00:52

标签: c++ c++11 stl

我正在尝试为所有可表示的有限值生成具有大致相等概率的随机双精度数。我相信这会像随机签名的exponential_distributionuniform_distribution将无效,因为可表示的双打不均匀分布。我有这个hacky代码似乎做我想要的:

#include <cmath>
#include <iostream>
#include <random>

template < class GEN >
double double_distribution (GEN& generator)
{
    using gen_type = typename GEN::result_type;
    static_assert (sizeof (gen_type) == sizeof (double), "");
    union {gen_type g; double f;} result;
    do {
        result.g = generator();
    } while (!std::isfinite (result.f));
    return result.f;
}

int main() {
    std::mt19937_64 mt ((std::random_device())());
    for (int n = 0; n < 10; ++n) {
        std::cout << double_distribution (mt) << ' ';
    }
    std::cout << '\n';
}

但是我想知道是否有一种更简单的方法来使用STL现有的RandomNumberDistribution类之一来估算它?

2 个答案:

答案 0 :(得分:0)

如果你假设一个64位IEEE双,这是一种方法。 (对于double的不同表示,您应该能够修改此基本方法。)

64位被分为1位用于符号,11位用于指数,52位用于分数。 NaN和inf等特殊值由指数位的特殊值表示。你有两个基本的选择:

  1. 从所有可能的64位组合的集合中生成一个随机数(如适当大小的整数类型上的统一),并从中设置双精度位。在这种情况下,您有时会绘制一个对应于例如NaN的数字。您需要丢弃这些并重绘或接受它们,具体取决于您是否要返回NaN。 (听起来你可能不喜欢你的情况。)

  2. 在一次绘制中生成符号和小数部分的位,然后分别绘制指数。如果你这样做,你应该能够修改你的绘图,这样你就不会拉出NaN值 - 特殊值似乎对应于指数的极值,所以这只会改变你绘制的范围在整数上,我认为许多软件包很容易支持它。在这种情况下,您不必重新绘制&#34;为了避免特殊的价值观,但你最终可能会得到一些&#34;额外的&#34;因为你可能最终从一个整数类型的分布(具有8个位数的多个)中拉出每个绘制的位,但是对于符号+分数使用53而对于指数仅使用11。你可能可以做一些聪明的事情来节省这些比特,以便&#34;抛出&#34;一次抽签中的位用于下一次抽奖。你必须仔细考虑,以确保这不会引起你的抽签之间的依赖,但我认为应该没问题。

  3. 如果效率对你来说并不重要,那么#1看起来更容易实现。如果你需要效率,那么也许#2可能会更好,尽管可能以更复杂的代码为代价。

答案 1 :(得分:-1)

您正在寻找的是std::uniform_real_distribution

请注意,这将 - 至少在Visual Studio上 - 适用于从numeric_limits<double>::lowest()numeric_limits<double>::max()的整个范围,因为计算涉及下部和上部之间的距离范围的边界,明显大于double允许的范围。

我建议你重新考虑一下你的方法,因为它可能实际上并不是你想要的 - 所有双值之间的均匀分布几乎只会产生价值具有极大的幅度(几乎所有你得到的数字都会变成某种东西 - * 10 ^ 300)。

无论如何,如果你真的想在这里使用统一分布,那就是一个例子:

#include <random>
#include <limits>
#include <iostream>
#include <cmath>

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    // sqrt so distance between min and max fits into a double
    auto max = std::sqrt(std::numeric_limits<double>::max());
    auto min = -max;
    std::cout << "min: " << min << " max: " << max << std::endl;
    std::uniform_real_distribution<double> dis(min, max);
    for (int n = 0; n < 100; ++n) {
        std::cout << dis(gen) << '\n';
    }
    std::cout << std::endl;
}