代码改变世界

单片机浮点乘除法优化措施及防止分子数据溢出

2021-10-06 11:17  斑鸠,一生。  阅读(1432)  评论(0编辑  收藏  举报

        单片机以性价比为特点,随着能源行业的发展,单片机在数字能源中的运用越来越广泛。最近学习了如何低端机上实现浮点运算。


1、前言

          目前,大多数的单片机不具有浮点运算单元(FPU)。

          TI公司的tms320f28335具有FPU,但是在进行一个浮点除法运算时,需要1.5us的时间,这在实时控制系统中是不太能接受的。

          不具有浮点运算的单片机,需要将浮点运算转换为可以接受的整形运算

2、整形运算概述

           单片机主要分为8位机、16位机和32位机,使用最多的就是16位机。

           16位机中变量类型 int 是16位的,也就是说:16位机使用16位的0或者1组合表示数据。

            32位机中变量类型 int 是32位的,也就是说:32位机使用32位的0或者1组合表示数据。

3、浮点数的近似转换

           在单片机中,浮点数乘除运算可以近似乘以一个整数然后除以2的n次方表示。

           例如:

           0.25 = 1 >> 2;

           0.5 = 1 >> 1;

           0.75 = 3 >>2;

            其他的浮点数以此类推。

            一个浮点数可以有多种近似替换的方案,每种替换方案的精度不一样。

            比如:

                     0.8 可以近似等于3>>2(0.75),也可以近似等于13>>4(0.8125);

                     明显可以看到,使用13>>4替换0.8比3>>2的精度要高些。但是前者更容易造成数据位溢出

 

4、防止数据溢出

            步骤三的转换也是有前提条件的:变量乘了整数后防止数据超过最大值。

            如果整形变量是16位的,那么它乘以一个数后,它也必须是16位的,不能超出原有变量的数据类型的范围。

            下面测试案例:

//定义变量类型
unsigned int   a;
unsigned int   b;
unsigned long  c;
unsigned int   d;

//测试方法
    a = (1024*1024)>>10;
    b = ((long)(1024*1024))>>10;
    c = (1024*1024)>>10;
    d = __builtin_muluu(1024,1024)>>10;

//编译警告
Test.c:101:15: warning: integer overflow in expression
Test.c:102:21: warning: integer overflow in expression
Test.c:103:14: warning: integer overflow in expression

//调试变量结果
a = 0
b = 0
c = 0
d = 1024

        在16位单片机的C编译器的作用下,由上面的测试结果显示,仅仅只有第四种输出正确结果。

        其他三种,不管是强制数据转换,还是设置为long型的变量,导致输出结果都不正确。

        可能:

              在这款单片机中,内存的最大位数位16位吧,如果需要超出16位运算结果的数据,就会报错。

              或者只能按照该单片机自带的函数处理数据吧。

5、与浮点Q变换的区别

          浮点数a转为定点数b是:b = (int)a*2n

          定点数b转为浮点数a是:a = (float)a>>n

          Q变换固定了n的位数,因此,截断误差也是固定的。一般,Q变换是把浮点数全部转为定点数参与运算,最后转为才转为浮点数;

          上述提到的变换,截断误差取决于具体乘以的数和参数n;,这种变换,参与运算的等效为一个浮点数。

 


 

注意事项:

        上述优化措施有一个很大的弊端,">>"运算很容易造成数据精度的丢失。

            当一个整数A,乘以一个比1/A还小的数的时候,经过位移运算后,变为零

              比如:

              对于系数0.75:

              1*0.75 = 0.75   转化为  1*3>>2 = 0;          丢失数值0.75,相对误差100%

              2*0.75 = 1.5     转化为  2*3>>2 = 1;      丢失数值0.5,相对误差为33.3%

              10*0.75 = 7.5   转化为 10*3>>2 = 7;         丢失数值0.5,相对误差为6.6%

              20*0.75 = 15    转化为 20*3>>2 = 15;      丢失数值0,相对误差为0%

              100*0.75 = 75  转化为100*3>>2=75;        丢失数值0,相对误差为0%

              200*0.75 = 150   转化为 200*3>>2 = 150;  丢失数值0,相对误差为0%

              对于系数0.25:

              1*0.25 = 0.25   转化为  1>>2 = 0;        丢失数值0.25,相对误差100%

              2*0.25 = 0.5    转化为  2>>2 = 0;      丢失数值0.5,相对误差为100%

              10*0.25 = 2.5   转化为 10>>2 = 2;       丢失数值0.5,相对误差为25%

              20*0.25 = 5      转化为 20>>2 =  5;      丢失数值0,相对误差为0%

              100*0.25 = 25   转化为100>>2=25;     丢失数值0,相对误差为0%;         

              200*0.25 = 50   转化为 200>>2 = 50;  丢失数值0,相对误差为0%

              对于系数0.125:

              1*0.125 = 0.125   转化为  1>>3 = 0;    丢失数值0.125,相对误差100%

              2*0.125 = 0.25      转化为  2>>3 = 0;丢失数值0.25,相对误差为100%

              8*0.125 =  1          转化为 8>>3 = 1;    丢失数值0,相对误差为0%

               9*0.125=1.125    转化为9>>3 = 1;       丢失数值0.125,相对误差为11.11%

              10*0.125 = 1.25   转化为 10>>3 = 1;  丢失数值0.25,相对误差为20%

              20*0.125 = 2.5       转化为 20>>3= 2;    丢失数值0,相对误差为20%

              100*0.125 = 12.5  转化为100>>3=12;    丢失数值0.5,相对误差为4%

              150*0.125 =18.75 转化为150>>3=18;   丢失数值0.75,相对误差为4%

               180*0.125=22.5  转换为180>>3=22;    丢失数值0.5,相对误差为2.22%

              200*0.125 = 25   转化为 200>>3= 25;        丢失数值0,相对误差为0%

 

        因此,

              上述思路,对于乘同以小于1的系数(被乘数),需要整数A不能太小,


            总结:分子防溢出,系数防丢失,我太难了。