|
本篇文章主要解决的问题是C语言整形与浮点型转化过程中的精度损失从而容易产生bug,对于浮点和整形的相关内容大家可以参考下面两篇文章<【重磅】“整形数”还真没那么简单(C语言版)>、<【典藏】别怪"浮点数"太坑(C语言版本)>,这里就不再赘叙了。 ; X! v. x# p+ W r 1 整形与浮点转化的精度损失参考小程序:1#include <stdio.h> 2#include <stdlib.h>) `+ X* R- A. g6 O! i7 U 3: w/ O# _! X2 K/ ]8 ~ 4/************************************* 5 * Fuction:精度损失 6 * Author : (公众号:最后一个bug) 7 ************************************/ 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 13 printf("sizeof(int) = %d\n",sizeof(int)); 14 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; 17 printf("fVal1 = %f\n",fVal1); 18 19 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; 24 printf("sVal1 = %d\n",sVal1); 25! 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 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 } 33 else 34 { 35 printf("sVal1 != sVal2\n"); 36 }) T n: @. l7 U% B3 [: k9 g 37( u& R& Y; o" t: ~, g 38 printf("公众号:最后一个bug\n"); 39# h @0 `3 _" h) G, S) B+ I2 T 40 return 0;% B. ^2 q- O; d# D5 ?! z 41} 运行结果:
分析一下:
2 没必要强制转化的地方 大家对数据进行强制类型转化很大部分都是为了方便计算,比如通过AD采样获得的数据都是整形的数据,通过比例因子处理以后成为了浮点数据,然后通过浮点进行计算实在是再方便不过了。 对于上面这种强制类型转化情景是值得考虑的,可是对于下面的场景就没有必要了,反而容易出现问题。 : m# C' U# q; O& ^
对于上面这种处理情景,估计很多小伙伴就直接通过整形接受到数据,然后强制类型转化为了浮点类型进行保存,当需要使用到该参数的时候,又通过把该浮点存储保存的值强制类型转化为整形,最后再进行整形参数的相关处理。 经过这样整形-->浮点-->整形的处理,如果你拿着最后的整形进行数据判断,就很大概率上会存在类似于前面的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/************************************* 5 * Fuction: 两个宏--可以继续扩展 6 * Author : (公众号:最后一个bug) 7 ************************************/ 8#define SET_INT_VAL(addr,val) *((int*)(&addr)) = val 9#define SET_GET_VAL(addr) *((int*)(&addr)) 108 d% o; [) h! f# m 11/*************************************+ U! w# d3 ~& P |( Y 12 * Fuction: 改善小技巧 13 * Author : (公众号:最后一个bug) 14 ************************************/ - {' 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 19 printf("sizeof(int) = %d\n",sizeof(int)); 20 printf("sizeof(float) = %d\n",sizeof(float)); 21 22 //接受整形数据到float数据内存 % {+ ^9 j; }1 ` 23 SET_INT_VAL(fVal1,sVal1); ; {$ _8 i2 d2 w/ Y9 r" R5 E 24 25 //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 31 return 0; X7 S8 M( l9 h2 {6 s 32} 运行结果:
分析一下:
4、最后小结 今天主要是跟大家分享了一个小案例,大家好好理解下,C语言虽然语法不多,不过想要写出好的C代码并非易事,需要多练多悟!作者也会慢慢回顾这些年的编程经验并分享给大家。 |
| 学习了 |
微信公众号
手机版