我能做到的最好的就是这个(结果在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 :(得分:3)
由于对双数进行编码的方式,很难直接在(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]。
答案 1 :(得分:1)
根据我的收集和判断,仅是想将uint_64
的位映射到double
的位,以使您的double
介于[0,1]
双(64位)表示是
-1S×(1.0 + 0.M)×2E偏差(偏差为1023)
符号S为1位,尾数为52位,指数为11位。
首先对double
进行了签名,所以由于您希望double
> 0.0,因此无法映射符号位。
其次,我们不能超过E-bias> 1022,否则我们将获得超过1.0的值double
。
因此,用于将uint_64映射为[0,1]之间的两倍的掩码是
0 01111111110 1111111111111111111111111111111111111111111111111111111 十六进制为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]上将不是均匀的!
答案 2 :(得分:0)
这就是我用的 我移位12得到52位 我或与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;
}