轉自:http://heyshadow.blog.163.com/blog/static/575033432007112731119269/
【文章標題】: C語言中的四舍五入問題
【文章作者】: sneer
【作者郵箱】: heyshadow@163.com
【作者主頁】: heyshadow.blog.163.com
【作者QQ號】: 281580801
【作者聲明】: 歡迎光臨sneer的小窩`
--------------------------------------------------------------------------------
【詳細過程】
這是 C 語言中最經常被提出的問題之一,這個問題在 C FAQ 列表中的問題 14.6 中有過簡短的討論。其中的解答給出了如下的表達式:
(int)(x+0.5)
但也指出這種方法對負數無效。此外,一個更為明智的方法是用 long 替代 int。
下面我們來更進一步地解釋這個問題。
首先,這個問題的解決方案取決於我們所需要的轉換方式:是截斷轉換還是舍入轉換;另一方面,它基本與我們需要轉換的浮點數類型無關──無論是 float 還是 double,甚至是 long double。
有時人們覺得一個浮點變量的值和一個整型變量的值可以完全一樣地可表示,你可能相信 x 值為 100.0 時我們只要把它轉換為整型就會得到整數 100。但在任何時候,你都不應依賴於期望一個浮點數的值能夠與一個整型數的值完全相等,你真正所需要的可能是四舍五入。
截斷轉換的意思是拋棄所有的小數部分,例如 3.9 將被轉換為 3,這種轉換是 C 語言中將浮點數轉換為整型數的默認方式,也就是說無論在什么時候,只要將浮點數轉換為整型數,采用的都是這種方式。關於這種轉換何時發生,有一些特殊的規定,這里我們僅指出賦值時所發生的轉換,比如
i = x;
其中 i 是一個整型數,x 是一個浮點數。當然,在顯式類型轉換的情況下,這樣的轉換也會發生,比如
(int) x
舍入轉換是指獲取與給定浮點數最為接近的整型數,因此 3.9 應被轉換為 4,這才是人們提出我們正在解決的這個問題時所真正需要的。對於舍入轉換,我們沒有直接的工具(比如運算符或者庫函數),嚴格地說,舍入轉換並非與 C 標准里所定義的轉換在同一意義下的轉換。
對於正的浮點數,最簡單的四舍五入方法就是用這樣的一個表達式
(long) (x+0.5)
但是如果表達式對負數有效的話會更好,即使在你看來負數的情況不會發生。這意味着你可以用一個條件表達式:
x >= 0 ? (long)(x+0.5) : (long)(x-0.5)
這個表達式的返回值就是與浮點變量 x 的值最接近的整數值。
如果需要大量地使用舍入轉換,則可以寫這樣的一個宏:
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
這可以在某種程度上使代碼更具可讀性。
注意到這樣的轉換會將 1.5 變為 2 但卻將 -1.5 變為 -2,那么對於這樣正好處於兩個整數之間的浮點數,你可能需要做一些其它的處理,但這在實際當中並不十分重要。
需要小心的是,將一個浮點數轉換為一個整型數可能導致上溢,但大多數的實現都沒有進行相關的判斷。用 long 替代 int 會給出一個更寬的范圍(建議使用 long),但仍然比浮點數的范圍要小得多。
如果效率不是至關重要的話,則可以定義這樣的一個函數(而不是簡單地寫一個 #define),使你的程序更具魯棒性:
long round(double x) {
assert(x >= LONG_MIN-0.5);
assert(x <= LONG_MAX+0.5);
if (x >= 0)
return (long) (x+0.5);
return (long) (x-0.5);
}
如果在意效率的話,可以寫這樣一個宏
#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
這要求在程序中有 #include <limits.h>,並且有一個處理錯誤的函數 error,其返回值為 long 類型。