你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言整形与浮点型转化过程中的精度损失

[复制链接]
gaosmile 发布时间:2020-8-2 23:30
    本篇文章主要解决的问题是C语言整形与浮点型转化过程中的精度损失从而容易产生bug,对于浮点和整形的相关内容大家可以参考下面两篇文章<【重磅】“整形数”还真没那么简单(C语言版)>、<【典藏】别怪"浮点数"太坑(C语言版本)>,这里就不再赘叙了。

1
整形与浮点转化的精度损失
参考小程序:
1#include <stdio.h>
2#include <stdlib.h>
3
4/*************************************
5 * Fuction:精度损失
6 * Author : (公众号:最后一个bug)
7 ************************************/
8 int main(int argc, char *argv[]) {
9    float fVal1 = 0;
10    int   sVal1 = 60012502;
11    int   sVal2 = 60012501;
12
13    printf("sizeof(int) = %d\n",sizeof(int));
14    printf("sizeof(float) = %d\n",sizeof(float));
15
16    fVal1 = (float) sVal1;
17    printf("fVal1 = %f\n",fVal1);
18
19    fVal1 = (float) sVal2;
20    printf("fVal1 = %f\n",fVal1);
21
22
23    sVal1 = (int) fVal1;
24    printf("sVal1 = %d\n",sVal1);
25
26    sVal2 = (int) fVal1;
27    printf("sVal2 = %d\n",sVal2);
28
29    if(sVal1 == sVal2)
30    {
31        printf("sVal1 == sVal2\n");     
32    }
33    else
34    {
35        printf("sVal1 != sVal2\n");
36    }
37
38    printf("公众号:最后一个bug\n");
39
40    return 0;
41}
运行结果:
微信图片_20200802232806.png
分析一下:
  • 我只想说真的牛,转来转去竟然使得两个不一样的数相等了,所以大家在平时的项目中进行强制类型转化可要慎重考虑下了。
  • 其发生该情况的主要原因是由于4字节float类型变量不能完全覆盖int类型的所有类型数值,所以在进行强制类型转化的过程中会产生精度上的损失,这样大家稍不留意就会有一些不必要的麻烦。

2
没必要强制转化的地方
    大家对数据进行强制类型转化很大部分都是为了方便计算,比如通过AD采样获得的数据都是整形的数据,通过比例因子处理以后成为了浮点数据,然后通过浮点进行计算实在是再方便不过了。
    对于上面这种强制类型转化情景是值得考虑的,可是对于下面的场景就没有必要了,反而容易出现问题。

微信图片_20200802232810.png

    对于上面这种处理情景,估计很多小伙伴就直接通过整形接受到数据,然后强制类型转化为了浮点类型进行保存,当需要使用到该参数的时候,又通过把该浮点存储保存的值强制类型转化为整形,最后再进行整形参数的相关处理。
    经过这样整形-->浮点-->整形的处理,如果你拿着最后的整形进行数据判断,就很大概率上会存在类似于前面的Demo程序那样存在bug。
   那么有一些小伙伴该问了,为什么不用整形存储呢 ? 用整形存储不就没这个问题了吗 ?当你真正进入工作岗位,可能某一天一个同事离职,把一堆摊子丢给你了,然而里面的数据结构等等处理,不是不想动,而是不敢动,往往新接手的代码把控能力有有限,可能不小心的参数配置都会导致程序奔溃,还真不敢大动作?所以还是技巧性的填好坑,等待下一次重构的机会。
3
处理小技巧
    其实对于内存上所保存的数据是没有具体类型的,所谓数据类型仅仅只是以怎样一种方式进行数据的访问罢了,我们通过指针来进行访问就很好的说明了这个问题,例如int *ptr,表示ptr所指向的地址以int类型进行访问,如果把ptr改为float类型即float *ptr,其便以float类型来访问地址所对应的内存。
    既然像第二小节说的float类型的参数保存区无法修改了,那么我们就想办法直接绕过float类型来进行数据访问。
参考demo:
1#include <stdio.h>
2#include <stdlib.h>
3
4/*************************************
5 * Fuction: 两个宏--可以继续扩展
6 * Author : (公众号:最后一个bug)
7 ************************************/
8#define SET_INT_VAL(addr,val)  *((int*)(&addr)) = val
9#define SET_GET_VAL(addr)      *((int*)(&addr))
10
11/*************************************
12 * Fuction: 改善小技巧
13 * Author : (公众号:最后一个bug)
14 ************************************/
15 int main(int argc, char *argv[]) {
16    float fVal1 = 0;
17    int   sVal1 = 60012502;
18
19    printf("sizeof(int)   = %d\n",sizeof(int));
20    printf("sizeof(float) = %d\n",sizeof(float));
21
22    //接受整形数据到float数据内存
23    SET_INT_VAL(fVal1,sVal1);
24
25    //float数据内存获得整形数据
26    printf("SET_GET_VAL(fVal1) = %d\n",SET_GET_VAL(fVal1));
27    printf("            sVal1  = %d\n",sVal1);
28
29    printf("公众号:最后一个bug\n");
30
31    return 0;
32}

运行结果:
微信图片_20200802232819.png
分析一下:
  • Demo中通过两个宏进行地址的类型处理转化,对float内存进行直接访问来保存整形数据,同样后面通过地址的直接访问来获得整形数据,这样就可以绕开float类型的处理问题。
  • 同时大家也要注意其本地类型占用字节大小(float)需>=所要转化的类型占用字节大小(int),否则会造成数据的篡改,这样就会造成更多麻烦。
  • 其实这里仅仅只用float进行存储参数的设计,原本就存在设计上的缺陷,可以通过共联体,或者无类型内存设计方案进行处理,这样就不会出现类似的问题了。

4、最后小结
    今天主要是跟大家分享了一个小案例,大家好好理解下,C语言虽然语法不多,不过想要写出好的C代码并非易事,需要多练多悟!作者也会慢慢回顾这些年的编程经验并分享给大家。

收藏 1 评论1 发布时间:2020-8-2 23:30

举报

1个回答
likang1202 回答时间:2020-8-3 09:40:11
学习了
mark.png

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版