嵌入式系统与单片机|技术阅读
登录|注册

您现在的位置是:嵌入式系统与单片机 > 技术阅读 > 没有DSP的浮点单元不能进行浮点运算?

没有DSP的浮点单元不能进行浮点运算?

最近在项目上需要控制直流无刷电机,使用到FOC及PID算法,进行FOC运算的时候需要用到三角函数和反三角函数,因此不可避免的就是浮点数的运算,而使用的MCU不带有浮点单元,因此需要使用Q格式来简化,提高计算速度。后期等项目结束后准备来详细分享一下FOC算法的实现,控制三相无刷直流电机,进而可以拓展使用机器人的关节电机及其驱动。今天的重点不在这里,还是回归主题介绍一Q格式以及其如何实现浮点数(定点数)的运算。


Q格式

Q格式是二进制的定点数格式,相对于浮点数,Q格式指定了相应的小数位数和整数位数,在没有浮点运算的平台上,可以更快地对浮点数据进行处理,以及应用在需要恒定分辨率的程序中(浮点数的精度是会变化的);需要注意的是Q格式是概念上小数定点,通过选择常规的二进制数整数位数和小数位数,从而达到所需要的数值范围和精度。

定点数:小数点位置为确定的。

浮点数:小数点位置可以改变。


Q数据的表示a) 范围和精度

定点数通常表示为Qm.n,其中m为整数个数,n为小数个数,其中最高位位符号位并且以二进制补码的形式存储;

        

              


无符号的用UQm.n表示;

范围:[02m−2n]

精度:2n

b) 推导

无符号Q格式数据的推导
这里以一个16位无符号整数为例,UQ9.7所能表示的最大数据的二进制形式如下图所示;

所以不难看出,UQ9.7的范围大小和精度;
根据等比数列求和公式得到,整数域最大值如下:

Sumi=28+27+26+25+24+23+22+21+20=291

小数域最大值如下:

Sumf=21+22+23+24+25+26+27=127

因此UQ9.7的范围满足 [0,2927]

有符号Q格式数据的推导
这里以一个16位有符号整数为例,UQ9.7所能表示的最大数据的二进制形式如下图所示;

所以不难求出,UQ9.7的范围大小和精度;
根据等比数列求和公式得到,整数域最大值如下:

Sumi=27+26+25+24+23+22+21+20 = 281

小数域最大值如下:

Sumf=21+22+23+24+25+26+27 = 127

因此Q9.7最大能表示的数为:2827

Q9.7所能表示的最小数据的二进制形式如下图所示;

可以从图中看到,该数表示为28;

补充一下:负数在计算机中是补码的形式存在的,补码=反码+1,符号位为1则表示为负数;那么-4该如何表示呢?以8 bit数据为例,如下所示;
          原码:0B 0000 100
          反码:0B 1111 011
          补码:0B 1111 100

综上,可以得到有符号Q9.7的范围是:[28,2827]


1.9.3 Q数据的运算a) 0x7FFF

最大数的十六进制为0x7FFF,如下图所示;

b) 0x8000

最小数的十六进制为0X8000,如下图所示;

上述这两种情况,下面都会用到。


c) 加法

加法和减法需要两个Q格式的数据定标相同,即Qm1.n1和Qm2.n2满足以下条件;

{m1=m2   n1=n2}

1.  int16_t q_add(int16_t a, int16_t b)  

2.  {  

3.      return a + b;  

4.  }  

上面的程序其实并不安全,在一般的DSP芯片具有防止溢出的指令,但是通常需要做一下溢出检测,具体如下所示;

1.  int16_t q_add_sat(int16_t a, int16_t b)  

2.  {  

3.      int16_t result;  

4.      int32_t tmp;  

5.    

6.      tmp = (int32_t)a + (int32_t)b;  

7.      if (tmp > 0x7FFF)  

8.          tmp = 0x7FFF;  

9.      if (tmp < -1 * 0x8000)  

10.        tmp = -1 * 0x8000;  

11.    result = (int16_t)tmp;  

12.  

13.    return result;  

14.}  


d) 减法

类似于加法的操作,需要相同定标的两个Q格式数进行相减,但是不会存在溢出的情况;

1.  int16_t q_sub(int16_t a, int16_t b)  

2.  {  

3.      return a - b;  

4.  }  


e) 乘法

乘法同样需要考虑溢出的问题,这里通过sat16函数,对溢出做了处理;

1.  // precomputed value:  

2.  #define K   (1 << (Q - 1))  

3.  // saturate to range of int16_t  

4.  int16_t sat16(int32_t x)  

5.  {  

6.      if (x > 0x7FFF) return 0x7FFF;  

7.      else if (x < -0x8000) return -0x8000;  

8.      else return (int16_t)x;  

9.  }  

10.  

11.int16_t q_mul(int16_t a, int16_t b)  

12.{  

13.    int16_t result;  

14.    int32_t temp;  

15.  

16.    temp = (int32_t)a * (int32_t)b; // result type is operand's type  

17.    // Rounding; mid values are rounded up  

18.    temp += K;  

19.    // Correct by dividing by base and saturate result  

20.    result = sat16(temp >> Q);  

21.  

22.    return result;  

23.}  


f) 除法

1.  int16_t q_div(int16_t a, int16_t b)  

2.  {  

3.      /* pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format) */  

4.      int32_t temp = (int32_t)a << Q;  

5.      /* Rounding: mid values are rounded up (down for negative values). */  

6.      /* OR compare most significant bits i.e. if (((temp >> 31) & 1) == ((b >> 15) & 1)) */  

7.      if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0)) {     

8.          temp += b / 2;    /* OR shift 1 bit i.e. temp += (b >> 1); */  

9.      } else {  

10.        temp -= b / 2;    /* OR shift 1 bit i.e. temp -= (b >> 1); */  

11.    }  

12.    return (int16_t)(temp / b);  

13.}  


1.9.4 常见Q格式的数据范围

定点数Xq和浮点数Xf转换的关系满足以下公式:

Xq=(int)Xf2n

Xf=(float)Xf2n

其中XqXq为Qm.nQm.n,m表示整数位数,n表示小数位数;

1.  #include <stdio.h>  

2.  #include <stdint.h>  

3.  #include <math.h>

4.  int main()  

5.  {  

6.      // 0111 1111 1111 1111  

7.      int16_t q_max = 32767; // 0x7FFF  

8.      // 1000 0000 0000 0000  

9.      int16_t q_min = -32768; // 0x8000  

10.    float f_max = 0;  

11.    float f_min = 0;  

12.    printf("\r\n");  

13.    for (int8_t i = 15; i>=0; i--) {  

14.        f_max = (float)q_max / pow(2,i);  

15.        f_min = (float)q_min / pow(2,i);  

16.  

17.        printf("\t| Q %d | Q %d.%d| %f | %f |\r\n",  

18.               i,(15-i),i,f_max,f_min);  

19.    }  

20.  

21.    return 0;  

22.}  

运行得到结果如下所示:


Q 格式

Qmn

Max

Min

Q 15

Q 0.15

0.999969

-1.000000

Q 14

Q 1.14

1.999939

-2.000000

Q 13

Q 2.13

3.999878

-4.000000

Q 12

Q 3.12

7.999756

-8.000000

Q 11

Q 4.11

15.999512

-16.000000

Q 10

Q 5.10

31.999023

-32.000000

Q 9

Q 6.9

63.998047

-64.000000

Q 8

Q 7.8

127.996094

-128.000000

Q 7

Q 8.7

255.992188

-256.000000

Q 6

Q 9.6

511.984375

-512.000000

Q 5

Q 10.5

1023.968750

-1024.000000

Q 4

Q 11.4

2047.937500

-2048.000000

Q 3

Q 12.3

4095.875000

-4096.000000

Q 2

Q 13.2

8191.750000

-8192.000000

Q 1

Q 14.1

16383.500000

-16384.000000

Q 0

Q 15.0

32767.000000

-32768.000000


注意在Q格式运算的时候,两者定标必须相同,对于数据的溢出检测也要做相应的处理。