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

How to generate uniform single precision floating point random number between 0 and 1 in FPGA?

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

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

除某些特殊情况外,IEEE-754 二进制浮点数的有效数被标准化为 [1,2)。要将整数转换为有效数,我们需要对其进行归一化,以便设置最高有效位。我们可以通过计算前导零位的数量,然后将数字左移该数量来做到这一点。还需要前导零的计数来调整指数。

一个binary32数的有效位由24位组成,其中只存储了23位;最高有效位(整数位)始终为 1,因此是隐式的。这意味着并非整数的所有 32 位都可以合并到 binary32 中,因此在将 32 位无符号整数转换为 24 位精度时通常 rounds。为了简化实现,在下面的代码中我简单地 truncate 通过截断最低有效八位,这对均匀分布应该没有明显影响。对于指数部分,我们可以将归一化步骤的调整与比例因子 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): %u\n", 
                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

种子值是从另一个随机数生成算法生成的,所以不要被这个搞糊涂了。

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