按位将随机值转换为 FP64,值在 0 到 1 之间

问题描述 投票:0回答:1

我正在用 Excel VBA 编写实验室数据处理脚本。 这个想法是使用 RtlGenRandom 获取改进的随机数。 困难在于 RtlGenRandom 以字节方式为字节数组播种。 因此,为了减少时间并保持“随机”分布,我想应用按位掩码将数字减少到 [0 到 1] 值。 下面是一个助记符解释,无论具体实现如何,我都想做什么:

longlong buffer
longlong mask
double rand_val
RtlGenRandom(buffer,8)
buffer=not(buffer or mask)
rand_val=typeless_copy(rand_val, buffer)

所以我有点迷失了那个掩码对于所述瓣膜截断的价值应该是多少。 按位,或 0xHex.

我想,应该是0xC000 0000 0000 0000,但是不对劲

random boolean bit-manipulation mask
1个回答
0
投票

你似乎忘记了 IEEE-754 浮点数的两个细节:

尾数以硬编码的 1 开头

当人类用科学计数法写数字时(例如 3.4567e89),粗体部分称为 significandmantissa。从表面上讲浮点数,我们常说它们存储指数和尾数;但严格来说这是错误的。除了指数,它们只存储符号和尾数的 fraction 部分。由于尾数应始终以单个非零数字开头,而浮点数使用二进制系统,因此浪费一位应该始终为

1
.

是没有意义的

对于大多数情况,我们可以把float想象成一个表单文档,比如...

             fraction    exponent>0
 (-1)^[] x 1.[][][][] x 2^([][][] - bias)
     sign

...如果你可以填写

[]
框,但不能更改尾数开头的
1.

因此,您的方法从区间 [0, 2).

生成数字

非均匀分布

由于指数表示,接近于零的浮点数较多。例如。对于 64 位浮点数(又名双精度数),区间 [0.5, 1.0) 包含 2^52 个浮点数,但区间 (0, 0.5) 包含 2^(10+52) - 2 个浮点数。因此,您的数字 way 更可能接近 0 而不是接近 1.

一个简单的解决方案

为了解决这两个问题,让我们退一步。浮点数可以非常接近地表示 10^-17 = 0.00000000000000001,但不能表示 0.5 + 10^-17 = 0.50000000000000001。如果我们生成前者的数字而不是后者的数字,那将是不公平的。因此,我们应该将区间[0, 1]平均分割成都可以精确表示的数。区间 [0, 1) 中最大的 ULP 是 2^-53 = ca. 10^-16 = 0.0000000000000001.

区间[0, 1)包含2^53个这样的数(

0
,
1 * 2^-53
,
2 * 2^-53
, ...,
(2^53 - 1) * 2^-53
)。因此我们生成 2^53 位整数并将其除以 2^53 以生成介于 0(含)和 1(不含)之间的随机数。

' pseudo code for generating one random Double
' from [0,1) with uniform distribution
LongLong buffer
RtlGenRandom(buffer, 8)
buffer = buffer and 0x1FFFFFFFFFFFFF  ' clamp to 53 bits    
Double rand = buffer / CDbl(0x20000000000000)

如果您确实也需要能够生成 1,则有 2^53 + 1 个可能的数字,在保持均匀分布的同时不能再轻易地限制这些数字。我相信拒绝抽样将是您案例中最简单(甚至可能最快)的解决方案。

' pseudo code for generating one random Double
' from [0,1] with uniform distribution
LongLong buffer
Double rand
Do
  RtlGenRandom(buffer, 8)
  buffer = buffer and 0x3FFFFFFFFFFFFF  ' clamp to 54 bits
Loop While buffer > 0x20000000000000
If buffer == 0x20000000000000 Then
  rand = 1.0
Else
  rand = buffer / CDbl(0x20000000000000)
End If

为了加快这两种方法的速度,最好只在非常大的缓冲区上调用一次

RtlGenRandom
。如果您知道需要 N 个随机数,则可以为第一种方法生成 8*N 个随机字节,或者为第二种方法生成 8*N*1.5(平均)个随机字节。由于每个 LongLong 只需要 7 个随机字节,您甚至可以将总缓冲区大小减少 1/8。

© www.soinside.com 2019 - 2024. All rights reserved.