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

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

[复制链接]
gaosmile 发布时间:2020-8-2 23:30
    本篇文章主要解决的问题是C语言整形与浮点型转化过程中的精度损失从而容易产生bug,对于浮点和整形的相关内容大家可以参考下面两篇文章<【重磅】“整形数”还真没那么简单(C语言版)>、<【典藏】别怪"浮点数"太坑(C语言版本)>,这里就不再赘叙了。
; X! v. x# p+ W  r
1
整形与浮点转化的精度损失
参考小程序:
1#include <stdio.h>
) V! a$ f' A8 i 2#include <stdlib.h>) `+ X* R- A. g6 O! i7 U
3: w/ O# _! X2 K/ ]8 ~
4/*************************************
) W& g0 G- I3 B. | 5 * Fuction:精度损失
: }2 g5 ]; o  j( J; p* H9 Q6 ?$ T 6 * Author : (公众号:最后一个bug)
2 ~0 t# A1 D' ]* J5 P- B& l/ ? 7 ************************************/
; T' i; g3 C; m" Q4 O1 n. W 8 int main(int argc, char *argv[]) {0 k; C& |4 P4 W" Q
9    float fVal1 = 0;5 i* A8 t! z3 {# x* l
10    int   sVal1 = 60012502;; W& c  Y* w7 R7 T
11    int   sVal2 = 60012501;4 d' x+ P8 u+ l2 F8 p, D; F. q
12
$ V. [1 z- j' Q13    printf("sizeof(int) = %d\n",sizeof(int));
$ L* g6 ^# o7 O8 U0 a! C14    printf("sizeof(float) = %d\n",sizeof(float));& E! Z( U: R8 y& o
15# P) g  J, G, M5 R+ P0 r
16    fVal1 = (float) sVal1;
6 }( [; K$ |3 l% I  G! w9 b7 T: Q7 W17    printf("fVal1 = %f\n",fVal1);
/ L. F9 J7 ^2 G4 w18
; E( J3 U' y2 E8 j19    fVal1 = (float) sVal2;$ j. o9 E! w) o$ D& x8 [
20    printf("fVal1 = %f\n",fVal1);' u9 r4 Y, n/ ]2 ?* b! f
212 j% V  ~( {- c$ z5 ?# _$ }$ e/ ]
22* @& E0 P7 g6 W
23    sVal1 = (int) fVal1;
3 h5 B3 w; Y4 g  |1 j; S24    printf("sVal1 = %d\n",sVal1);
6 z: d" d! v. b) _: E" X0 N- i25! n" e5 o* Z/ h  x0 k+ {
26    sVal2 = (int) fVal1;" D$ E- D3 m, L+ @. I
27    printf("sVal2 = %d\n",sVal2);$ K. q  N2 P  ?: T( g4 k. C
28
/ E# e  w+ V6 ]+ ^) }29    if(sVal1 == sVal2)0 O" S! Z  V" ], x! f
30    {5 |5 f  \. d+ g$ C  }7 a" u
31        printf("sVal1 == sVal2\n");     5 O3 r4 @, c! z( M8 s! [1 ]
32    }
' d* b% D6 J* }2 p' j4 @* @9 g6 J33    else
5 e1 H2 G, y. c5 g. y* l" |34    {
4 j9 \( R; L2 i" \+ i, E35        printf("sVal1 != sVal2\n");
' q" Z) N( o# h! i# j36    }) T  n: @. l7 U% B3 [: k9 g
37( u& R& Y; o" t: ~, g
38    printf("公众号:最后一个bug\n");
, a$ M5 g+ }. j" i8 G( Y39# h  @0 `3 _" h) G, S) B+ I2 T
40    return 0;% B. ^2 q- O; d# D5 ?! z
41}
运行结果:
微信图片_20200802232806.png
分析一下:
  • 我只想说真的牛,转来转去竟然使得两个不一样的数相等了,所以大家在平时的项目中进行强制类型转化可要慎重考虑下了。
  • 其发生该情况的主要原因是由于4字节float类型变量不能完全覆盖int类型的所有类型数值,所以在进行强制类型转化的过程中会产生精度上的损失,这样大家稍不留意就会有一些不必要的麻烦。
    4 [" ?- }7 R" V1 p
2
没必要强制转化的地方
    大家对数据进行强制类型转化很大部分都是为了方便计算,比如通过AD采样获得的数据都是整形的数据,通过比例因子处理以后成为了浮点数据,然后通过浮点进行计算实在是再方便不过了。
    对于上面这种强制类型转化情景是值得考虑的,可是对于下面的场景就没有必要了,反而容易出现问题。
: m# C' U# q; O& ^
微信图片_20200802232810.png

8 D$ `* A  @7 J; `
    对于上面这种处理情景,估计很多小伙伴就直接通过整形接受到数据,然后强制类型转化为了浮点类型进行保存,当需要使用到该参数的时候,又通过把该浮点存储保存的值强制类型转化为整形,最后再进行整形参数的相关处理。
    经过这样整形-->浮点-->整形的处理,如果你拿着最后的整形进行数据判断,就很大概率上会存在类似于前面的Demo程序那样存在bug。
   那么有一些小伙伴该问了,为什么不用整形存储呢 ? 用整形存储不就没这个问题了吗 ?当你真正进入工作岗位,可能某一天一个同事离职,把一堆摊子丢给你了,然而里面的数据结构等等处理,不是不想动,而是不敢动,往往新接手的代码把控能力有有限,可能不小心的参数配置都会导致程序奔溃,还真不敢大动作?所以还是技巧性的填好坑,等待下一次重构的机会。
3
处理小技巧
    其实对于内存上所保存的数据是没有具体类型的,所谓数据类型仅仅只是以怎样一种方式进行数据的访问罢了,我们通过指针来进行访问就很好的说明了这个问题,例如int *ptr,表示ptr所指向的地址以int类型进行访问,如果把ptr改为float类型即float *ptr,其便以float类型来访问地址所对应的内存。
    既然像第二小节说的float类型的参数保存区无法修改了,那么我们就想办法直接绕过float类型来进行数据访问。
参考demo:
1#include <stdio.h>" z& q, H6 `0 q2 p5 ~7 L& n, ^
2#include <stdlib.h>( u/ f  J1 S; h8 y
31 T& Q7 I/ K5 W# M
4/*************************************
& d. c2 H: w( v4 _6 ~ 5 * Fuction: 两个宏--可以继续扩展
% A5 E/ {( C  S' w& X7 ^ 6 * Author : (公众号:最后一个bug)
# N. ?$ @* i2 E) V8 v 7 ************************************/
7 C/ b) |2 w* }' r7 ]7 {: @ 8#define SET_INT_VAL(addr,val)  *((int*)(&addr)) = val
* t: R7 ?5 z  ?) Z  O+ Q* @( } 9#define SET_GET_VAL(addr)      *((int*)(&addr))
2 h0 y5 |2 z; O! f3 c  d108 d% o; [) h! f# m
11/*************************************+ U! w# d3 ~& P  |( Y
12 * Fuction: 改善小技巧
# x& I0 w, Y0 c- I  X13 * Author : (公众号:最后一个bug)
, V7 ?4 c  v2 H5 y. n14 ************************************/ - {' v# I  k/ w. B8 ^5 W
15 int main(int argc, char *argv[]) {+ E) c6 S. H4 ?3 n3 Y
16    float fVal1 = 0;- K, C& j: w4 S# a- G' n6 L
17    int   sVal1 = 60012502;4 Z  W( D( b/ e0 ^8 M6 f7 j6 k6 s& Y/ y
18
2 X' e' f) E+ C% W0 |7 l19    printf("sizeof(int)   = %d\n",sizeof(int));
. \* B! y( ?, K5 y0 E) @4 d20    printf("sizeof(float) = %d\n",sizeof(float));
# Z) F' c( z9 g21
8 t5 ?; b. T  v- I9 ]7 g22    //接受整形数据到float数据内存 % {+ ^9 j; }1 `
23    SET_INT_VAL(fVal1,sVal1); ; {$ _8 i2 d2 w/ Y9 r" R5 E
24
0 j% u/ d- U1 P25    //float数据内存获得整形数据 . t! R) O. i, r, ~
26    printf("SET_GET_VAL(fVal1) = %d\n",SET_GET_VAL(fVal1)); # k3 z$ N# a& x1 D3 |2 A5 |# H' I- a
27    printf("            sVal1  = %d\n",sVal1); ) U, a1 @6 f# A5 R; x
28& `5 J8 E) i# P: U: _
29    printf("公众号:最后一个bug\n"); & n/ o5 M- s- P, ^
30
0 K2 \% y8 u; `3 D/ o# P31    return 0;  X7 S8 M( l9 h2 {6 s
32}

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

    1 S2 o% \1 ^" q/ K
4、最后小结
/ D4 [3 x9 L# T# o* x
    今天主要是跟大家分享了一个小案例,大家好好理解下,C语言虽然语法不多,不过想要写出好的C代码并非易事,需要多练多悟!作者也会慢慢回顾这些年的编程经验并分享给大家。

1 d* V  @% j) Q! a
赞 收藏 1 评论1 发布时间:2020-8-2 23:30

举报

1个回答
李康1202 回答时间:2020-8-3 09:40:11
学习了
mark.png
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版