轉自http://www.delphitop.com/html/jichu/153.html 感謝原作者。
這段時間在用DELPHI做一個財務系統時發現每一行的小計取了兩位小數後與用SQL的ROUND查詢出來的不一樣,在程式中是用FormatFloat('0.00',ItemSum)函式來取值的,再用DXDBGRID網格顯視合計,最終與SELECT SUM(ROUND(ITEMSUM,2))得出的結果是不一樣的。
例如ItemSum:=1.005,在程式FormatFloat('0.00',ItemSum)就返回是“1”,應該是“1.01”才是。
因為以前做的系統都沒有用DXDBGIRD來顯視合計, 一直都沒留意這個問題,然後新建了一個程式來測試,用FormatFloat('0.00',1.005)得出是“1.01”是正確的,這是怎麼回事呢,將1.005付給變數ItemSum再FormatFloat('0.00',ItemSum)就返回是“1”,單步除錯時ItemSum一直都是1.005,透過變數同一個資料就有不同的結果。
一開始ItemSum是Double型別的,後來改為Extended後FormatFloat('0.00',ItemSum)就返回是“1.01”了,為什麼會這樣呢,後來想了一下可能是Double型別,因為在計算機中數是用二進位制儲存的,而十進位制的有限小數轉換成二進位制可能變成無限小數,所以儲存的時候會有一定的誤差。在通常情況下,如果要比較兩個數的大小,必須透過對它們的求差得到,如比較ItemSum1與ItemSum是否相等,應用abs(ItemSum1-ItemSum1)<0來判斷。1.005就變成了1.004999999999999....了。
使用Extended或者FormatFloat('0.00',StrToFloat(FlotToStr(ItemSum)))就解決問題了。
**************************
在DELPHI中Round()和RoundTo()都是四捨五入的函式,但DELPHI用的四捨五入與一般的四捨五入不同,它採用的是四捨六入五留雙。
即當舍或入位大於或小於五時按四捨五入來處理,而當舍或入位等於五時,就要看前面一位是什麼,根據奇進偶不進,它總是返回一個偶數值。
這種演算法其實是按照銀行家演算法,統計學上一般都用這種演算法,比傳統的"四捨五入"要科學。在VB、.net相關的語言中都有這個問題。
例如:
表示式 返回值
Round(11.5) 12
Round(10.5) 10
RoundTo(1234567, 3) 1234000
RoundTo(1.234, -2) 1.23
RoundTo(1.235, -2) 1.24
RoundTo(1.245, -2) 1.24
如果要使用傳統的"四捨五入"方法,可以使用下面函式:
function RoundEx(R: Real): Int64;
begin
Result:= Trunc(R);
if Frac(R) >= 0.5 then
Result:= Result + 1;
end;
function DRound(const Value: Extended; const Digit: Byte = 0): Extended;
var
tmp: Extended;
begin
tmp := Power(10, Digit);
if Value > 0 then
Result := Value * tmp + 0.5
else
Result := Value * tmp - 0.5;
Result := Trunc(Result) / tmp;
end;