跳转至

浮点数与定点数

Abstract

浮点数的运算开销较大,在 FPGA 上往往需要用定点数来代替浮点数,虽然在精度上会有所损失,但是可以大大提高运算速度。

参考:

原理

浮点数转化为定点数

  1. 决定定点格式: \(Q_{m,n}\) (其中 m 表示整数部分位数,n 表示小数部分位数)
  2. 将浮点数乘以 2 的 \(n\) 次方(放大操作),以把要转换的浮点数移动到整数部分。
  3. 最后进行取整操作(一般采用四舍五入方法)。

假设我们要表示的数是 \(x\),其 IEEE 形式为 \(2^{exp}\times 1.frac\),记作 \(x_f\)。要找到其对应的定点数表示 \(x_d\)

根据定义, \(x_d\) 相当于是最低 \(n\) 位表示小数部分,那么我们让 \(x_d \times 2^n\) 就得到了一个整数,我们记作 \(y\),可以看到 \(y\) 的正常二进制表示和 \(x_d\) 是相同的(即定点表示 \(x_d\) 对应的二进制串等价于 \(x\times 2^n\) 的普通二进制表示)

我们对 \(x_f\) 也乘上 \(2^n\),这样得到了 \(2^{exp+n}\times 1.frac\),此时四舍五入,将 \(frac\) 部分中前 \(n+exp\) 位以后的位全部归 0(因为前 \(n+exp\) 位乘上前面的幂后得到的是整数,后面的部分仍然是小数,这样在四舍五入时就被抹去了)。

四舍五入后,我们将这个数转化为整数类型,此时就从 IEEE 浮点类型转化为了正常的二进制表示,这就是 \(y\),我们可以直接把它作为 \(x\) 的定点数表示。

Examples

  • 假设我们要将浮点数转为 \(Q_{1, 14}\) 格式的定点数,代码如下:

    float limit = 1.99993896484375;
    int16_t float_to_q14_f(double x) {
        return ((int16_t)((x) <= limit ? ((x) >= -limit ? (x)*0x7FFF : -0x7FFF) : 0x7FFF));
    }
    

    这里我们要先判断 x 是否超过我们能表示的范围。对于 \(Q_{1,14}\) 我们用最高位来表示正负号,次高位表示整数部分,剩下 14 位表示小数。那么所能表示的最大的数是 \((1.11111111111111)_2\),即小数点后 14 个 1,表示的数是 \(1.99993896484375\)。对于超过范围的数,我们直接返回定点数所能表达的最值,否则我们将输入乘上 \(2^{14}\)

  • 假设我们输入的是 32 位浮点数,要将其转为 \(Q_{1,31}\) 格式的定点数,代码如下:

    /*
    x: float
    输出 32 位定点数,其中最高位为整数位,剩余 31 位为小数位
    */
    uint32_t float_to_int32(float x) {
        if (x >= 1.0) return (1<<31);
        return (uint32_t)((x) * 0x80000000);
    }
    
    • x=0.5 时函数返回 0x4000000
    • x=0.67 时函数返回 0x55c28f80=0101 0101 1100 0010 1000 1111 1000 0000,计算可得这个定点数表示的是 0.670000016689300537109375

    上述的代码常用于对数据预处理时,我们可以将数据先归一化到 \([0,1]\) 范围上,随后就可以转为 32 位定点,只需要 1 位来存储整数位,不会有较大的精度损失。

Hints

  • 转化过程中可能会有溢出。
  • 如何四舍五入。
  • 定点数运算时:需要考虑溢出、÷0 和舍入误差;对乘除等运算时需要关注精度。
  • 对于有符号数,需要留出一位给符号位。

最后更新: 2023年12月7日 15:10:29
创建日期: 2023年12月7日 15:10:29

评论