如何在FPGA中生成0到1之间的均匀单精度浮点随机数?



我试图通过生成0和0x3f80000之间的数字来使用FPGA生成单精度浮点随机数(IEEE格式为1)。但是由于接近零的离散点数量多于1,所以我没有得到统一的生成。有没有什么变换可以用来模拟均匀生成。我使用LFSR(32位)和Xoshiro随机数生成。

从均匀分布的32位无符号整数生成[0,1)中均匀分布的floats的标准方法是将整数乘以2-32。显然,我们不会仅仅为了这个目的在FPGA上实例化一个浮点乘法器,我们也不必这样做,因为乘法器是2的幂。实际上,需要做的是将整数转换为浮点数,然后将浮点数的指数减32。这不适用于必须作为特殊情况处理的零输入。在下面的ISO-C99代码中,我假设float映射为IEEE-754binary32类型。

除某些特殊情况外,IEEE-754二进制浮点数的有效位数归一化为[1,2]。要将一个整数转换为有效数,我们需要对其进行规范化,以便设置最高有效位。我们可以通过计算前导0位的个数,然后向左移动这个数字来实现这一点。也需要前导零的计数来调整指数。

binary32数的有效位为24位,其中仅存储23位;最高有效位(整数位)总是1,因此是隐式的。这意味着并不是所有的32位整数都可以被合并到binary32中,所以在转换32位无符号整数时,通常舍入到24位精度。为了简化实现,在下面的代码中,我只是通过截断最不有效的8位来截断,这应该对均匀分布没有明显的影响。对于指数部分,我们可以将归一化步骤的调整与比例因子2-32的减法结合起来。

下面的代码是使用以硬件为中心的原语编写的。提取一个比特只是抓住正确的电线的问题,固定数量的移动同样只是简单的电线移动。计算前导零个数的电路通常称为优先级编码器。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define USE_FP_MULTIPLY  (0)
uint32_t bit (uint32_t, uint32_t);
uint32_t mux (uint32_t, uint32_t, uint32_t);
uint32_t clz (uint32_t);
float uint32_as_float (uint32_t);
/* uniform float in [0, 1) from uniformly distributed random integers */
float uniform_rand_01 (uint32_t i)
{
const uint32_t FP32_EXPO_BIAS = 127;
const uint32_t FP32_MANT_BITS = 24;
const uint32_t FP32_STORED_MANT_BITS = FP32_MANT_BITS - 1;
uint32_t lz, r;
// compute shift amount needed for normalization
lz = clz (i);
// normalize so that msb is set, except when input is zero
i = mux (bit (lz, 4), i << 16, i);
i = mux (bit (lz, 3), i <<  8, i);
i = mux (bit (lz, 2), i <<  4, i);
i = mux (bit (lz, 1), i <<  2, i);
i = mux (bit (lz, 0), i <<  1, i);
// build bit pattern for IEEE-754 binary32 floating-point number
r = (((FP32_EXPO_BIAS - 2 - lz) << FP32_STORED_MANT_BITS) + 
(i >> (32 - FP32_MANT_BITS)));
// handle special case of zero input
r = mux (i == 0, i, r);
// treat bit-pattern as 'float'
return uint32_as_float (r);
}
// extract bit i from x
uint32_t bit (uint32_t x, uint32_t i)
{
return (x >> i) & 1;
}
// simulate 2-to-1 multiplexer: c ? a : b ; c must be in {0,1}
uint32_t mux (uint32_t c, uint32_t a, uint32_t b)
{
uint32_t m = c * 0xffffffff;
return (a & m) | (b & ~m);
}
// count leading zeros. A priority encoder in hardware.
uint32_t clz (uint32_t x)
{
uint32_t m, c, y, n = 32;
y = x >> 16; m = n - 16; c = (y != 0); n = mux (c, m, n); x = mux (c, y, x);
y = x >>  8; m = n -  8; c = (y != 0); n = mux (c, m, n); x = mux (c, y, x);
y = x >>  4; m = n -  4; c = (y != 0); n = mux (c, m, n); x = mux (c, y, x);
y = x >>  2; m = n -  2; c = (y != 0); n = mux (c, m, n); x = mux (c, y, x);
y = x >>  1; m = n -  2; c = (y != 0); n = mux (c, m, n - x);
return n;
}
// re-interpret bit pattern of a 32-bit integer as an IEEE-754 binary32 
float uint32_as_float (uint32_t a)
{
float r;
memcpy (&r, &a, sizeof r);
return r;
}
// George Marsaglia's KISS PRNG, period 2**123. Newsgroup sci.math, 21 Jan 1999
// Bug fix: Greg Rose, "KISS: A Bit Too Simple" http://eprint.iacr.org/2011/007
static uint32_t kiss_z=362436069, kiss_w=521288629;
static uint32_t kiss_jsr=123456789, kiss_jcong=380116160;
#define znew (kiss_z=36969*(kiss_z&65535)+(kiss_z>>16))
#define wnew (kiss_w=18000*(kiss_w&65535)+(kiss_w>>16))
#define MWC  ((znew<<16)+wnew )
#define SHR3 (kiss_jsr^=(kiss_jsr<<13),kiss_jsr^=(kiss_jsr>>17), 
kiss_jsr^=(kiss_jsr<<5))
#define CONG (kiss_jcong=69069*kiss_jcong+1234567)
#define KISS ((MWC^CONG)+SHR3)
#define N 100
uint32_t bucket [N];
int main (void)
{
for (int i = 0; i < 100000; i++) {
uint32_t i = KISS;
#if USE_FP_MULTIPLY
float r = i * 0x1.0p-32f;
#else // USE_FP_MULTIPLY
float r = uniform_rand_01 (i);
#endif // USE_FP_MULTIPLY
bucket [(int)(r * N)]++;
}
for (int i = 0; i < N; i++) {
printf ("bucket [%2d]: [%.5f,%.5f): %un", 
i, 1.0f*i/N, (i+1.0f)/N, bucket[i]);
}
return EXIT_SUCCESS;
}

请查看此处的xoshiro128+ https://prng.di.unimi.it/xoshiro128plus.c

有人写的VHDL代码可以在这里找到:https://github.com/jorisvr/vhdl_prng/tree/master/rtl

种子值是由另一个随机数生成算法生成的,所以不要被这个混淆了。

根据使用的种子值,它应该给出一个均匀的分布。

最新更新