0

私は現在、重要度のサンプリングに取り組んでおり、テスト目的uniform_real_distribution<float>で、間隔 [0,1] で生成される可能性のあるすべての値を生成できる必要があります (はい、右からも閉じています)。私のアイデアは、浮動小数点数に変換できる整数を生成することでした。私が行ったテストから、[0,1] の一様な単精度浮動小数点数と [0,2^24] の整数の間に完全な全単射があるようです ([0,1] ではないという事実に少し悩まされています,2^24-1] そして、私はまだ理由を理解しようとしています.0は浮動小数点数にとって単に特別であり、1から2^24はすべて同じ指数を持つ浮動小数点数になるというのが私の最善の推測です)。私の質問は、この方法で生成された float が から生成できる float であるかどうかuniform_real_distribution<float>です。私の整数 <-> フロート テストは以下のとおりです。

void floatIntegerBitsBijectionTest()
{
    uint32 two24 = 1 << 24;
    bool bij24Bits = true;
    float delta = float(1.0) / float(two24);
    float prev = float(0) / float(two24);
    for (uint32 i = 1; i <= two24; ++i)
    {
        float uintMap = float(i) / float(two24);
        if (uintMap - prev != delta || uint32(uintMap*float(two24)) != i)
        {
            std::cout << "No bijection exists between uniform floats in [0,1] and integers in [0,2^24].\n";
            bij24Bits = false;
            break;
        }
        prev = uintMap;
    }
    if(bij24Bits) std::cout << "A bijection exists between uniform floats in [0,1] and integers in [0,2^24].\n";
    std::cout << "\n";

    uint32 two25 = 1 << 25;
    bool bij25Bits = true;
    delta = float(1.0) / float(two25);
    prev = float(0) / float(two25);
    for (uint32 i = 1; i <= two25; ++i)
    {
        float uintMap = float(i) / float(two25);
        if (uintMap - prev != delta || uint32(uintMap*float(two25)) != i)
        {
            std::cout << "No bijection exists between uniform floats in [0,1] and integers in [0,2^25].\n";
            if (i == ((1 << 24) + 1)) std::cout << "The first non-uniformly distributed float corresponds to the integer 2^24+1.\n";

            bij25Bits = false;
            break;
        }
        prev = uintMap;
    }
    if (bij25Bits) std::cout << "A bijection exists between uniform floats in [0,1] and integers in [0,2^25].\n";
    std::cout << "\n";


    bool bij25BitsS = true;
    delta = 1.0f / float(two24);
    prev = float(-two24) / float(two24);
    for (int i = -two24+1; i <= two24; ++i)
    {
        float uintMap = float(i) / float(two24);
        if (uintMap - prev != delta || int(uintMap*float(two24)) != i)
        {
            std::cout << i << " " << uintMap - prev << " " << delta << "\n";
            std::cout << "No bijection exists between uniform floats in [-1,1] and integers in [-2^24,2^24].\n";
            bij25BitsS = false;
            break;
        }
        prev = uintMap;
    }
    if (bij25BitsS) std::cout << "A bijection exists between uniform floats in [-1,1] and integers in [-2^24,2^24].\n";
}

編集:

やや関連:

https://crypto.stackexchange.com/questions/31657/uniformly-distributed-secure-floating-point-numbers-in-0-1

http://xoroshiro.di.unimi.it/random_real.c

https://www.reddit.com/r/programming/comments/29ducz/obtaining_uniform_random_floats_is_trickier_than/

https://lemire.me/blog/2017/02/28/how-many-floating-point-numbers-are-in-the-interval-01/

編集2:

私は最終的に、デフォルトのテンプレート引数を使用uniform_real_distribution<float>した場合に少なくともmt19937エンジンで使用した場合に何が行われるかを理解することができました (VS2017 に付属する実装について話している)。悲しいことに、[0,2^32-1] でランダムな整数を生成し、それを float にキャストしてから 2^32 で割ります。言うまでもなく、これにより不均一に分散された浮動小数点数が生成されます。ただし、生成された数値間のデルタの精度に近い精度で作業していない限り、これはほとんどの実用的な目的で機能すると推測しています。

4

2 に答える 2

1

問題を強制することができます。独自のランダムフロートジェネレーターをロールします。

編集:std::generate_canonical<float>()同じことをするが、マジックナンバー24に依存しないことを発見しました。それは、std::numerical_limits<float>::digitsなどからうまくいきます...

#include <random>

static const unsigned long big = 1 << 24;
static std::default_random_engine re;
static std::uniform_int_distribution<unsigned long> uint(0, big - 1);

float rand_float() {
    return uint(re) / static_cast<float>(big);
}
于 2018-02-25T00:40:17.637 に答える