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

问题描述

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

解决方法

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

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

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,2),i <<  4,1),i <<  2,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,x);
    y = x >>  8; m = n -  8; c = (y != 0); n = mux (c,x);
    y = x >>  4; m = n -  4; c = (y != 0); n = mux (c,x);
    y = x >>  2; m = n -  2; c = (y != 0); n = mux (c,x);
    y = x >>  1; m = n -  2; c = (y != 0); n = mux (c,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",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

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

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...