问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32浮点型变量数值大小边界判断有误的问题的原因及解决方案

创作时间:
作者:
@小白创作中心

STM32浮点型变量数值大小边界判断有误的问题的原因及解决方案

引用
CSDN
1.
https://blog.csdn.net/weixin_53534399/article/details/137927800

在使用STM32G431RBT6芯片进行开发时,遇到一个关于浮点型变量边界判断的问题。本文详细分析了问题的原因,并提供了具体的解决方案,对于从事嵌入式开发的工程师具有一定的参考价值。

1. 问题描述

在使用STM32G431RBT6芯片的开发板时,需要实现一个按键控制浮点型变量累加与到达边界值清零的功能。具体功能如下图所示:

只关于这一部分功能的代码段如下:

float x = 0;				// 浮点型变量初始化
/* 中间的其它代码与该问题无关,这里省略,只保留功能相关代码 */
if(ucKey_Down == 1)			// 按下B1
{
    if(x < 3.3)
    {
        x += 0.3;
    }
    else if(x >= 3.3)
    {
        x = 0;
    }
}

但是当使用LCD将该值输出时,发现了一个奇怪的现象:变量x竟然增加到了3.6后才清零。

2. 发现原因

通过调试功能,发现了问题的根源。原来当LCD显示x值为3.3的时候,x的实际值是3.29999971。对于程序中的判断而言,这个值仍然比3.3小,因此下一次按下按键时,变量还会继续增加0.3,导致最终显示3.6的结果。

这种现象的原因在于浮点型变量的存储方式。浮点型变量与其他变量一样,都是按照二进制存储的。整数部分的数值存储可以实现十进制与二进制的完全无误差转化,但小数部分的存储精度是基于二进制的,最小计数单位是2的负几次方。因此,0.3这个数值在存储时本身就带有误差。同样的,如果浮点型变量的初始化值的小数部分不能表示成2的负整数幂次之和的形式,那么初始化值同样会在小数的最后几位出现误差,例如:

float x = 2.4;

这种误差在正常计算和显示数值时可以通过四舍五入忽略,但在边界判断时,这种微小的误差可能会对程序产生决定性的影响。

3. 检验猜测

考虑到浮点型变量的小数部分的存储数值为2的负整数幂指数的计算精度,如果小数部分可以表示为若干个2的负幂指数之和的形式,应该就可以实现完全无误差转化。因此,修改递增数值重新测试,修改后的代码如下:

float x = 0;				// 浮点型变量初始化
/* 中间的其它代码与该问题无关,这里省略,只保留功能相关代码 */
if(ucKey_Down == 1)			// 按下B1
{
    if(x < 3.375)
    {
        x += 0.375;
    }
    else if(x >= 3.375)
    {
        x = 0;
    }
}

由于0.375可以表示为2的负幂指数之和(0.375 = 2^{-1} + 2^{-2}),调试代码时发现当x加到最大值时得到结果x=3.375,且没有误差。这验证了前面关于原因的猜测是正确的。

4. 解决方法

为了避免存储误差带来的赋值细微误差造成的边界判断出错的问题,可以在边界判断时进行宽松处理。具体来说:

  • 当判断条件中是小于号“<”接边界值时,即if(x<bound),对边界值在功能误差容许范围内进行细微减小处理。例如,将前面的判断3.3为x的上界的代码改写为:
float x = 0;				// 浮点型变量初始化
/* 中间的其它代码与该问题无关,这里省略,只保留功能相关代码 */
if(ucKey_Down == 1)			// 按下B1
{
    if(x < 3.29)
    {
        x += 0.3;
    }
    else if(x >= 3.29)
    {
        x = 0;
    }
}

这样就可以实现在x理论值为3.3时,再次按下按键B1,x将回到0,实现了符合要求的循环递增。

  • 如果判断的条件是大于边界值,那么就作增大判断边界值的宽松处理。例如:
float x = 3.0;				// 浮点型变量初始化
/* 中间的其它代码与该问题无关,这里省略,只保留功能相关代码 */
if(ucKey_Down == 1)			// 按下B1
{
    if(x >= 0.01)
    {
        x -= 0.3;
    }
    else if(x < 0.01)
    {
        x = 3.3;
    }
}

这样就可以实现每次按下B1按键x递减0.3,直到减到理论值0时,x变为3.3,实现循环递减。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号