繁体   English   中英

如何使用按位运算将随机 uint64_t 转换为范围 (0, 1) 内的随机双精度值

[英]How to convert random uint64_t to random double in range (0, 1) using bit wise operations

我能达到的最好的是这个(它给出的结果在 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 之间表示的所有值。

由于双数的编码方式,很难直接在 (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]。

从我收集的内容来看,仅从您想将uint_64的位映射到double位的问题判断,这样您的double介于 [0, 1] 之间

双(64 位)表示是

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

符号 S 的一位,matissa 的 52 位和指数的 11 位。

第一个double是有符号的,因此无法映射符号位,因为您想要double >0.0。 其次,我们不能超过 E-bias >1022 否则我们将得到超过 1.0 的值double

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

0 01111111110 111111111111111111111111111111111111111111111111111 十六进制 0x3FEFFFFFFFFFFFFFull

因此代码

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

应该做的伎俩。 使用这种方法,您已经使用了 uint_64 的 64 位中的 61 位。

但请注意:如果您的uint_64生成器在 [0, uint64_t_max] 上是均匀的,则使用to01获得的生成器在 [0,1] 上将不均匀!

这就是我使用的 I 移位 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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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