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

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

本文关键字:随机 范围 双精度 何使用 运算 uint64 转换      更新时间:2023-10-16

我能达到的最好的结果是这个(它给出的结果在 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);
}

我反复试验并错误地找出这些神奇的数字。我什至不知道这个是否可以生成双精度可以在 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 的位,马蒂萨的 52 位和指数的 11 位。

double是有符号的,因此无法映射符号位,因为您需要double>0.0。 其次,我们不能超过 E 偏差>1022,否则我们将得到超过 1.0 的值double.

因此,在 [0, 1] 之间映射uint_64一倍的掩码为

0 01111111110 1111111111111111111111111111111111111111111111111111 在十六进制中

0x3FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF因此代码

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

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

但请注意:如果您的uint_64发生器均匀超过 [0, uint64_t_max] 使用to01获得的发生器将不会均匀超过 [0,1] !

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