std::uniform_real_distribution
.
STL 在今年的 Going Native 会议上有一个非常好的演讲,解释了为什么应该尽可能使用标准发行版。简而言之,手工编写的代码往往质量差得可笑(想想std::rand() % 100
),或者有更微妙的一致性缺陷,例如(std::rand() * 1.0 / RAND_MAX) * 99
在演讲中给出的示例,并且是问题中发布的代码的一个特例。
编辑:我查看了 libstdc++ 的实现std::uniform_real_distribution
,这就是我发现的:
该实现通过对 range 中生成的某个数字[dist_min, dist_max)
使用简单的线性变换来生成 range 中的数字[0, 1)
。它使用 生成此源编号std::generate_canonical
,我可以在此处找到其实现(在文件末尾)。确定分布范围的std::generate_canonical
次数(表示为k
),表示为整数,此处表示为*,将适合目标类型的尾数。然后它所做的基本上是为尾数的每个大小的段生成一个数字,并使用算术相应地填充每个段。结果值的公式可以表示为r
[0, r)
r
Σ(i=0, k-1, X/(r^i))
其中X
是 中的随机变量[0, r)
。范围的每个除法相当于用于表示它的位数(即log2(r)
)的移位,因此填充相应的尾数段。这样,使用了目标类型的全部精度,并且由于结果的范围是[0, 1)
,因此指数保持为0
**(模偏差),并且当您开始弄乱时,您不会遇到一致性问题指数。
我不相信这种方法在密码学上是安全的(而且我怀疑在计算 的大小时可能会出现错误r
),但我想它在一致性方面比你的 Boost 实现更可靠张贴,绝对比摆弄std::rand
.
可能值得注意的是,Boost 代码实际上是该算法的退化情况,其中k = 1
,这意味着如果输入范围需要至少 23 位来表示其大小(IEE 754 单精度)或至少 52 位,则它是等效的(双精度)。这意味着最小范围分别为~840 万或~4.5e15。根据这些信息,我认为如果您使用二进制生成器,Boost 实现将不会减少它。
在简要了解libc++ 的实现之后,看起来它们使用的是相同的算法,但实现方式略有不同。
(*)r
实际上是输入的范围加一。这允许使用max
urng 的值作为有效输入。
(**) 严格来说,编码的指数不是0
,因为 IEEE 754 在有效数字的基数之前编码了一个隐含的前导 1。然而,从概念上讲,这与该算法无关。