在 0 和 1 之间生成随机双精度数
需要考虑的一些事项:
包括 1.0??
我希望目标是“在 [0, 1) 范围内”,而不是“在 [0, 1] 范围内”。包含 1.0 会造成复杂性,因为它会使分布失衡。让我们假设 [0, 1)。
rand()质量
rand() 具有未指定的质量,其范围 [0...RAND_MAX] 可能不会以小于 2 的幂的 1 结尾。让我们假设 rand() 现在已经足够好了,RAND_MAX 是一个 Mersenne Number(这很常见),以便通过一个简单的 ^ 加入多个 rand() 呼叫。
小数字
大约一半的正面double 小于 1.0。他们都应该有机会吗?
典型值 DBL_MANT_DIG 是 53,所以:
在 [0.5...1.0) 范围内有 252double 值。
在 [0.25...0.5) 范围内有 252double 值。
在 [0.125...0.25) 范围内有 252double 值。
...
我们是否希望有 50% 的机会来自第一组,25% 来自下一组,12.5% 来自下一组......?
或满意:
表格253double值在[0.0...1.0)范围内均匀分布?
现在让我们实现第二个目标 - 更容易编码。
在 [0...253) 范围内生成一个整数 double,然后除以 253。
RAND_MAX >= 0x7FFF 根据定义,所以我们得到至少 15 位的随机位。
下面是一些说明性代码,它们形成double [0.0 ... 1.0),效率不高。
// Illustrative code
double rand_double_01(void) {
unsigned long long r = 0;
#define RANDOM_BITS 15
for (int i = 0; i < DBL_MANT_DIG; i += RANDOM_BITS) {
r <<= RANDOM_BITS;
r ^= rand();
}
r %= 1uLL << DBL_MANT_DIG; // Mask off lower 53 bits
double dr = r; // expected conversion is exact
// scale [0 ... 1.0)
dr /= 1uLL << DBL_MANT_DIG; // expected conversion/quotient exact
return double dr;
}
注意:当DBL_MANT_DIG >= 64(不常见)或FLT_RADIX != 2(非常不常见)时,上述代码可能会失败。