代码之家  ›  专栏  ›  技术社区  ›  J.Doe

如何使用逐位运算将随机uint64\t转换为范围(0,1)中的随机双精度

c++
  •  0
  • J.Doe  · 技术社区  · 7 年前

    我能达到的最好结果是这个(它给出的结果在0~0.5之间):

    uint64_t const FRACTION_MASK = 0x1fffffffffffffull;
    uint64_t const EXPONENT_BITS = 0x3fc0000000000000ull;
    double to01(uint64_t i) {
        i = (i & FRACTION_MASK) | EXPONENT_BITS;
        return reinterpret_cast<double&>(i);
    }
    

    我试着找出那些神奇的数字。我甚至不知道这个是否能产生一个double可以在0到0.5之间重复的所有值。

    2 回复  |  直到 7 年前
        1
  •  3
  •   Evg    7 年前

    由于双数的编码方式,很难直接在(0,1)范围内生成一个好的随机数,但在[1,2][@GemTaylor]范围内很容易得到一个。

    double to_01(uint64_t i)
    {
        constexpr uint64_t mask1 = 0x3FF0000000000000ULL;
        constexpr uint64_t mask2 = 0x3FFFFFFFFFFFFFFFULL;
        const uint64_t to_12 = (i | mask1) & mask2;
        double d;
        memcpy(&d, &to_12, 8);
        return d - 1;
    }
    

    注意,不是所有的 i 用于生成结果。另外请注意,此函数仅适用于64位IEEE-754浮点数[@geza]。

        2
  •  1
  •   PilouPili    7 年前

    从我收集到的信息,以及你想要描绘的问题判断 uint_64 double 这样你的 双重的

    需要双(64位)表示

    -1S(1.0+0.M)2E偏差(偏差为1023)

    一位表示符号S,52位表示matissa,11位表示指数。

    弗斯特 双重的 双重的 其次,我们不能超过E-bias>1022,否则我们将获得超过1.0的值 双重的 .

    因此,将uint_64映射到[0,1]之间的double的掩码是

    十六进制0x3FEFFFFFFFFFFFFFull

    uint64_t const MASK = 0x3FEFFFFFFFFFFFFFull;
    double to01(uint64_t i) {
        i = (i & MASK);
        return reinterpret_cast<double&>(i);
    }
    

    应该会成功的。在这个方法中,您使用了uint\u64的64位中的61位。

    但要注意的是:如果 uint\ U第64页 生成器在[0,uint64\t\u max]上是均匀的 to01

        3
  •  1
  •   lkreinitz    5 年前

    这就是我用的 我移12得到52位 I或1,这样我只得到奇数值这避免了返回0 然后在返回时减去1.0

    如果你知道整数的来源是均匀分布的

    double convert_uint64_to_double(uint64_t value)
    {
        uint64_t u64 = 0x3FF0000000000000ULL | ((value >> 12) | 1) ;
        return *(double*)&u64 - 1.0;
    }
    
    double convert_uint32_to_double(uint32_t value)
    {
        uint64_t u64 = 0x3FF0000000000000ULL | (((uint64_t(value) << 1) | 1) << 19);
        return *(double*)&u64 - 1.0;
    }