畢業設計的圖像檢索系統演算法無誤,但特徵值與相似度顯示卻出現負數。使用歐式距離計算,懷疑是數值過大導致有號整數溢位。本文分析溢位成因、驗證方法,並給出 unsigned、long long、浮點數等多種修正策略。
sqrt(Σ(xi - yi)²),平方項極易超出 32 位有號整數範圍(-2,147,483,648 ~ 2,147,483,647)。int 或 long(VC++ 6.0/2010 中長度同 32 位),累加時發生溢位,最高位變成符號位,導致結果變負。TRACE 或 printf 印出中間變數原始位元模式(%08X)。0x80000000 以上數值(即最高位為 1)。| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
unsigned int / unsigned long | 數值不超過 4,294,967,295 | 改動最小,效能最好 | 仍有上限,負數語意消失 |
long long / __int64 | 需支援更大整數(至 9×10¹⁸) | 範圍極大,保留有號語意 | VC6 需用 __int64,格式化字串為 %I64d |
double / float | 允許小數、極大動態範圍 | 彈性最大,科學計算標準 | 精度問題、效能稍低 |
| 大整數庫(GMP、Boost.Multiprecision) | 任意精度需求 | 無上限 | 引入依賴、效能顯著下降 |
// 原始碼(易溢位)
int sum = 0;
for (int i = 0; i < n; ++i) {
int diff = feat1[i] - feat2[i];
sum += diff * diff; // 這裡溢位
}
double dist = sqrt(sum);
// 修正版:使用 double 累加,避免中間溢位
double sum = 0.0;
for (int i = 0; i < n; ++i) {
double diff = static_cast(feat1[i]) - feat2[i];
sum += diff * diff;
}
double dist = sqrt(sum);
printf("%.6f", dist); 固定 6 位小數CString str; str.Format(_T("%.4f"), dist); MFC 專用%llu(unsigned long long)或 %I64u(VC6)
數值溢位是 C/C++ 經典坑,核心在於「型別範圍 < 實際數值範圍」。優先改用 double 進行科學計算,若追求效能且確定範圍,unsigned long long 是次佳選。記得同步修正顯示格式化字串,避免「算對了卻印錯」的二次烏龍。