如何在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) 中均匀分布的 float
s 的标准方法是将整数乘以 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
种子值是从另一个随机数生成算法生成的,所以不要被这个搞糊涂了。
根据使用的种子值,它应该给出一个均匀分布。
我正在尝试通过生成 0 到 0x3f80000 之间的数字(IEEE 格式为 1)来使用 FPGA 生成单精度浮点随机数。但是由于接近 0 的离散点数量多于 1,所以我没有得到统一的生成。有没有我可以应用的任何转换来模拟统一生成。我正在使用 LFSR(32 位)和 Xoshiro 随机数生成。
从均匀分布的 32 位无符号整数生成 [0,1) 中均匀分布的 float
s 的标准方法是将整数乘以 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
种子值是从另一个随机数生成算法生成的,所以不要被这个搞糊涂了。
根据使用的种子值,它应该给出一个均匀分布。