【C進(jìn)階】這種地方別再?gòu)?qiáng)制類型轉(zhuǎn)化了,來(lái)告訴你個(gè)小技巧!
掃描二維碼
隨時(shí)隨地手機(jī)看文章
本篇文章主要解決的問(wèn)題是C語(yǔ)言整形與浮點(diǎn)型轉(zhuǎn)化過(guò)程中的精度損失從而容易產(chǎn)生bug,對(duì)于浮點(diǎn)和整形的相關(guān)內(nèi)容大家可以參考下面兩篇文章<【重磅】“整形數(shù)”還真沒(méi)那么簡(jiǎn)單(C語(yǔ)言版)>、<【典藏】別怪"浮點(diǎn)數(shù)"太坑(C語(yǔ)言版本)>,這里就不再贅敘了。
1
參考小程序:
1#include <stdio.h>
2#include <stdlib.h>
3
4/*************************************
5 * Fuction:精度損失
6 * Author : (公眾號(hào):最后一個(gè)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("公眾號(hào):最后一個(gè)bug\n");
39
40 return 0;
41}
運(yùn)行結(jié)果:
分析一下:
我只想說(shuō)真的牛,轉(zhuǎn)來(lái)轉(zhuǎn)去竟然使得兩個(gè)不一樣的數(shù)相等了,所以大家在平時(shí)的項(xiàng)目中進(jìn)行強(qiáng)制類型轉(zhuǎn)化可要慎重考慮下了。
其發(fā)生該情況的主要原因是由于4字節(jié)float類型變量不能完全覆蓋int類型的所有類型數(shù)值,所以在進(jìn)行強(qiáng)制類型轉(zhuǎn)化的過(guò)程中會(huì)產(chǎn)生精度上的損失,這樣大家稍不留意就會(huì)有一些不必要的麻煩。
2
大家對(duì)數(shù)據(jù)進(jìn)行強(qiáng)制類型轉(zhuǎn)化很大部分都是為了方便計(jì)算,比如通過(guò)AD采樣獲得的數(shù)據(jù)都是整形的數(shù)據(jù),通過(guò)比例因子處理以后成為了浮點(diǎn)數(shù)據(jù),然后通過(guò)浮點(diǎn)進(jìn)行計(jì)算實(shí)在是再方便不過(guò)了。
對(duì)于上面這種強(qiáng)制類型轉(zhuǎn)化情景是值得考慮的,可是對(duì)于下面的場(chǎng)景就沒(méi)有必要了,反而容易出現(xiàn)問(wèn)題。
對(duì)于上面這種處理情景,估計(jì)很多小伙伴就直接通過(guò)整形接受到數(shù)據(jù),然后強(qiáng)制類型轉(zhuǎn)化為了浮點(diǎn)類型進(jìn)行保存,當(dāng)需要使用到該參數(shù)的時(shí)候,又通過(guò)把該浮點(diǎn)存儲(chǔ)保存的值強(qiáng)制類型轉(zhuǎn)化為整形,最后再進(jìn)行整形參數(shù)的相關(guān)處理。
經(jīng)過(guò)這樣整形-->浮點(diǎn)-->整形的處理,如果你拿著最后的整形進(jìn)行數(shù)據(jù)判斷,就很大概率上會(huì)存在類似于前面的Demo程序那樣存在bug。
那么有一些小伙伴該問(wèn)了,為什么不用整形存儲(chǔ)呢 ? 用整形存儲(chǔ)不就沒(méi)這個(gè)問(wèn)題了嗎 ?當(dāng)你真正進(jìn)入工作崗位,可能某一天一個(gè)同事離職,把一堆攤子丟給你了,然而里面的數(shù)據(jù)結(jié)構(gòu)等等處理,不是不想動(dòng),而是不敢動(dòng),往往新接手的代碼把控能力有有限,可能不小心的參數(shù)配置都會(huì)導(dǎo)致程序奔潰,還真不敢大動(dòng)作?所以還是技巧性的填好坑,等待下一次重構(gòu)的機(jī)會(huì)。
3
其實(shí)對(duì)于內(nèi)存上所保存的數(shù)據(jù)是沒(méi)有具體類型的,所謂數(shù)據(jù)類型僅僅只是以怎樣一種方式進(jìn)行數(shù)據(jù)的訪問(wèn)罷了,我們通過(guò)指針來(lái)進(jìn)行訪問(wèn)就很好的說(shuō)明了這個(gè)問(wèn)題,例如int *ptr,表示ptr所指向的地址以int類型進(jìn)行訪問(wèn),如果把ptr改為float類型即float *ptr,其便以float類型來(lái)訪問(wèn)地址所對(duì)應(yīng)的內(nèi)存。
既然像第二小節(jié)說(shuō)的float類型的參數(shù)保存區(qū)無(wú)法修改了,那么我們就想辦法直接繞過(guò)float類型來(lái)進(jìn)行數(shù)據(jù)訪問(wèn)。
參考demo:
1#include <stdio.h>
2#include <stdlib.h>
3
4/*************************************
5 * Fuction: 兩個(gè)宏--可以繼續(xù)擴(kuò)展
6 * Author : (公眾號(hào):最后一個(gè)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 : (公眾號(hào):最后一個(gè)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 //接受整形數(shù)據(jù)到float數(shù)據(jù)內(nèi)存
23 SET_INT_VAL(fVal1,sVal1);
24
25 //float數(shù)據(jù)內(nèi)存獲得整形數(shù)據(jù)
26 printf("SET_GET_VAL(fVal1) = %d\n",SET_GET_VAL(fVal1));
27 printf(" sVal1 = %d\n",sVal1);
28
29 printf("公眾號(hào):最后一個(gè)bug\n");
30
31 return 0;
32}
運(yùn)行結(jié)果:
分析一下:
Demo中通過(guò)兩個(gè)宏進(jìn)行地址的類型處理轉(zhuǎn)化,對(duì)float內(nèi)存進(jìn)行直接訪問(wèn)來(lái)保存整形數(shù)據(jù),同樣后面通過(guò)地址的直接訪問(wèn)來(lái)獲得整形數(shù)據(jù),這樣就可以繞開(kāi)float類型的處理問(wèn)題。
同時(shí)大家也要注意其本地類型占用字節(jié)大小(float)需>=所要轉(zhuǎn)化的類型占用字節(jié)大小(int),否則會(huì)造成數(shù)據(jù)的篡改,這樣就會(huì)造成更多麻煩。
其實(shí)這里僅僅只用float進(jìn)行存儲(chǔ)參數(shù)的設(shè)計(jì),原本就存在設(shè)計(jì)上的缺陷,可以通過(guò)共聯(lián)體,或者無(wú)類型內(nèi)存設(shè)計(jì)方案進(jìn)行處理,這樣就不會(huì)出現(xiàn)類似的問(wèn)題了。
4、最后小結(jié)
今天主要是跟大家分享了一個(gè)小案例,大家好好理解下,C語(yǔ)言雖然語(yǔ)法不多,不過(guò)想要寫(xiě)出好的C代碼并非易事,需要多練多悟!作者也會(huì)慢慢回顧這些年的編程經(jīng)驗(yàn)并分享給大家。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!