float 與 int 相乘產生的令人崩潰的“ 2.3 * 10 = 22 ”

越是簡單的越不能忽視
類型強轉一定要 注意! 注意! 注意!

以上兩天結論前前後後花了我三天工作時間才讓我意識到並學到。
有的時候,不管你小心還是無意,坑就在那裏,踩的人夠多,也許就填平了。
在工作中,分配到一個bug,簡單描述一下:該程序很龐大,由QT編寫,跨平臺,支持ubuntu和windows,其中一個模塊需要控制海康攝像頭,代碼中有海康的SDK。bug的現象是無法設定到指定的攝像頭調焦倍數與焦距,表現就是攝像頭模糊不清,無法正常拍攝,且只在windows下發生。
本文由以下部分組成:

  • 調試步驟
  • SDK驗證
  • 2.3 * 10 = 22

調試步驟

工程代碼缺陷

bug剛剛拿到手,看了遍代碼,一個顯而易見的缺陷暴露出來:

  • 開環式代碼

什麼是開環式代碼?
簡單來看,這部分代碼就是這樣的:

Created with Raphaël 2.2.0開始輸入倍數焦距設置倍數焦距結束

缺陷有3:

  1. 沒判斷設定值是否合法
  2. 沒檢查設置是否成功
  3. 沒有防禦機制,沒有給出設置失敗的處理辦法

於是添加:

  • 合法性檢查
  • 開定時循環檢查
  • 定時器中檢查不成功重設

疑似SDK缺陷

同樣的代碼,在不同的系統中表現不同。
ubuntu 14.04 : 正常
windows7 : **異常 **
採用排除法,代碼中邏輯處理都是一致的,唯一不同的就是海康的sdk,一個是linux64版本,一個是win32版本。
運行時一個加載.so庫,一個加載dll。
那麼就來找找SDK的茬吧

SDK驗證

此處略,詳見:

2.3 * 10 = 22

查來查去,最終比對自己的驗證sdk的demo與工程代碼,發現最可疑的就是, 聚焦倍數(zoom值)的處理是有差異,驗證demo中直接使用float,而工程代碼中會強制轉換int,便於顯示以及處理,可我恰恰沒想到,就這個強制轉是問題所在,確切的說,是不同系統下的處理是問題原因所在。
先列出罪魁禍首:
zoom = rOpticalZoomLevelP * 10;

再來看一下其中變量的聲明與定義:
int zoom;
float rOpticalZoomLevelP = 0.0f;

使用qDebug()打印發現:
rOpticalZoomLevelP*10之前rOpticalZoomLevelP值是2.3
而在之後強轉賦值給zoom後,zoom是22。
也就是說:
2.3 * 10 = 22???

ubuntu下

系統:ubuntu16.04
gcc版本:gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)
demo:

int main(int argc, char **argv)
{
	float a = 2.3f;
    int b =0;
    b = a*10;
    printf("b ------>%d\n",b);
    return 0;
}

result:
這裏寫圖片描述

windows7下

系統:windows7
gcc版本:MinGW 4.9.1 32bit MinGW
demo:

int main(int argc, char **argv)
{
	float a = 2.3f;
    int b =0;
    b = a*10;
    printf("b ------>%d\n",b);
    return 0;
}

result:
b -------> 22

問題原因暫時未知,但是可以規避,使用qRound()函數:

zoom = qRound(qreal(rOpticalZoomLevelP*10))

原因

爲什麼會出現這樣的情況呢,因爲 a 是一個float型的數據,我們看它是2.3,但是它在內存裏就不一定是2.3,它可以是2.29999999999999999,或者2.30000000000000001,因爲浮點型的範圍爲-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章