[英]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.