嵌入式软件开发的基础知识(4):数据表示和计算(1)
因为浮点数运算的时间开销非常大,所以在嵌入式几乎不会使用浮点数进行运算。都是采用的定点运算。这时对数据的格式就需要一个很大的要求。
定点表示:定点的小数点是确定的,浮点数的小数点是浮动的。
举个栗子:
如果变量A为Uint8类型数据。将小数点设定在低3位处,则记A数据为Q3格式数据。高5位表示整数,低3位表示小数。小数的精度为:2^(-3), 也就是精度为0.125。整数部分最大值为:2^5-1=31。所以A的取值范围为:0~31.875。
当然存在夸张的成分,但是实际上应用中,一般是Uint32去表示Q10,Q16,Q24等常见的定点数据。也有Uint64表示Q30,Q38等数据。Qx后面的数字就表示使用低x位来表示小数。
定点运算:定点格式数据的加减乘除
加/减法:量纲相同就直接相加/减法,不同需要移位来转换量纲
乘法:Qm*Qn = Q(m+n):例如Q3*Q5=Q8
举个例子:
为了方便计算,我们使用Uint8类型数据作为例子,做一个Q4的运算(低四位表示小数)
上述的式子中可以看出,定点计算出来的Q8数据和原数据是一致的。但是转换到Q4之后,导致了精度损失,计算误差为0.046875。(因为Q4的精度为0.0625)这个误差至多为:1个精度(即0.0625)。精度损失是不可避免的,必然会向下取整(精度的整数倍)。
所以我们可以使用四舍五入,将误差控制在半个精度之内。四舍五入结果为:5.8125。四舍五入前的误差为:0.046875。经过了四舍五入之后,误差为:0.015625。
简化一下四舍五入的方式(针对正数):对小数点后一个bit加1。
若不满5:则bit位为0。加1之后,在位移之后会被舍弃。表现为四舍。
若满5:则bit位为1,加1之后会进位。所以右移之后,表现为五入。
好了,看似皆大欢喜了!!!可是仔细观察这个c_Q8。数据已经达到了1484,如果使用Uint8来定义,那这早就溢出导致输出错误了。所以我们还需要另外的处理。
定点乘法运算处理:
我们可以在小数点处截断,将a_Q4分为整数部分a1_Q0和小数部分a2_Q4计算。b_Q4同理。注意:量纲不同不能直接相加,但是可以直接相乘
这样的下来的结果仍然是93,也就是5.8125。在计算过程中由于分开计算,使用Uint8绝对不会溢出(只针对本情况)。
同理uint32的Q16格式,使用定点运算也永远不会溢出。但是Uint32的Q24格式则不一定,因为两个小数相乘最大可以乘到Q48,导致溢出。这个时候小数相乘的部分需要额外的缩放处理。会造成额外的精度偏差。但是小数部分相乘本来就会右移24位。如果不最处理的话,最终结果精度可以保证在一个精度以内,对其做四舍五入操作,最终的的精度仍然能够达到半个精度。
最后说一句,我们程序全是定点运算,不存在浮点数。EtherCat,Profinet之类的标准数据也全都是定点数,不存在浮点数。所以源头上没有浮点数,也不要在程序中声明和使用浮点数。

写东西实在是耗时