bestlong 怕失憶論壇

 

 

搜索
bestlong 怕失憶論壇 論壇 Delphi Round 函數不正確的四捨五入狀況
樓主: bestlong
go

Round 函數不正確的四捨五入狀況 [複製鏈接]

Rank: 9Rank: 9Rank: 9

6#
發表於 2011-3-8 12:01 |只看該作者
這是在 Delphi.KTop 新出現與四捨五入相關的問題, 發現用 1213.245 值先放到 Double 型態的變數內然後再傳入 SimpleRoundTo() 還是會發生四捨六入五成雙的狀況, 所以也是自行寫函數處理, 程式碼如下:
  1. function ArithmeticRoundTo(Value : double; Digit : integer) : double;
  2. var txt : string;
  3. begin
  4.   Digit := Abs(Digit); //不接受負數
  5.   txt := FormatFloat('#0.' + DupeString('0', Digit+1), Value); //例如 2 位時,用第 3 位來判斷,所以 Digit + 1
  6.   if txt[Length(txt)] = '5' then //最後一位遇 5 就變 9,則 SimpleRoundTo()一定會進位
  7.     txt[Length(txt)] := '9';
  8.   Result := SimpleRoundTo(StrToFloat(txt), -Digit);
  9. end;
複製代碼
不過這樣用格式化字串再做識別的方法, 在執行效能上應該會有較多的負擔.

資料來源 http://delphi.ktop.com.tw/board. ... d=69&tid=102040

Rank: 9Rank: 9Rank: 9

7#
發表於 2011-7-13 08:30 |只看該作者
參考來源 http://d.02t.cn/html/jczs/1188.html

DELPHI四舍五入问题解决

这段时间在用 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

如果要使用传统的"四舍五入"方法,可以使用下面函数:
  1. function RoundEx(R: Real): Int64;
  2. begin
  3.   Result := Trunc(R);
  4.   if Frac(R) >= 0.5 then
  5.     Result := Result + 1;
  6.   end;

  7. function DRound(const Value: Extended; const Digit: Byte = 0): Extended;
  8. var
  9.   tmp: Extended;

  10. begin
  11.   tmp := Power(10, Digit);
  12.   if Value > 0 then
  13.     Result := Value * tmp + 0.5
  14.   else
  15.     Result := Value * tmp - 0.5;
  16.   Result := Trunc(Result) / tmp;
  17. end;
複製代碼
我是雪龍
http://blog.bestlong.idv.tw
http://www.bestlong.idv.tw

Rank: 9Rank: 9Rank: 9

8#
發表於 2012-11-12 14:38 |只看該作者
  1. function RoundF(const Value: Extended; const Digit: Byte = 0): Extended;
  2. var
  3.   tmp: Extended;
  4. begin
  5.   tmp := Power(10, Digit);
  6.   if Value > 0 then
  7.     Result := Value * tmp + 0.5
  8.   else
  9.     Result := Value * tmp - 0.5;
  10.   Result := Trunc(Result) / tmp;
  11. end;

  12. procedure TForm1.FormCreate(Sender: TObject);
  13. var
  14. //  f1, f2: Double;
  15.   f1, f2: Extended;
  16. begin
  17.   Memo1.Clear;
  18.   f1 := 1213.235;
  19.   f2 := 1213.245;
  20.   Memo1.Lines.Append(FloatToStr(f1));
  21.   Memo1.Lines.Append(FloatToStr(f2));
  22.   Memo1.Lines.Append('1213.235 => ' + FloatToStr(RoundF(1213.235, 2)));
  23.   Memo1.Lines.Append('1213.245 => ' + FloatToStr(RoundF(1213.245, 2)));
  24.   Memo1.Lines.Append(FloatToStr(RoundF(f1, 2)));
  25.   Memo1.Lines.Append(FloatToStr(RoundF(f2, 2)));
  26.   Memo1.Lines.Append('');
  27.   f1 := -1213.235;
  28.   f2 := -1213.245;
  29.   Memo1.Lines.Append(FloatToStr(f1));
  30.   Memo1.Lines.Append(FloatToStr(f2));
  31.   Memo1.Lines.Append('-1213.235 => ' + FloatToStr(RoundF(-1213.235, 2)));
  32.   Memo1.Lines.Append('-1213.245 => ' + FloatToStr(RoundF(-1213.245, 2)));
  33.   Memo1.Lines.Append(FloatToStr(RoundF(f1, 2)));
  34.   Memo1.Lines.Append(FloatToStr(RoundF(f2, 2)));
  35.   Memo1.Lines.Append('');
  36.   f1 := 1213.5;
  37.   f2 := 1214.5;
  38.   Memo1.Lines.Append(FloatToStr(f1));
  39.   Memo1.Lines.Append(FloatToStr(f2));
  40.   Memo1.Lines.Append('1213.5 => ' + FloatToStr(RoundF(1213.5)));
  41.   Memo1.Lines.Append('1214.5 => ' + FloatToStr(RoundF(1214.5)));
  42.   Memo1.Lines.Append('1213.5 => ' + FloatToStr(RoundF(f1)));
  43.   Memo1.Lines.Append('1214.5 => ' + FloatToStr(RoundF(f2)));
  44.   Memo1.Lines.Append('1213.5 => ' + FloatToStr(Round(1213.5)));
  45.   Memo1.Lines.Append('1214.5 => ' + FloatToStr(Round(1214.5)));
  46.   Memo1.Lines.Append('1213.5 => ' + FloatToStr(Round(f1)));
  47.   Memo1.Lines.Append('1214.5 => ' + FloatToStr(Round(f2)));
  48. end;
複製代碼
在傳入值 f1 與 f2 使用 Extended 型態得到下圖的結果:
arg-use-extended-type.jpg

而傳入值 f1 與 f2 使用 Double 型態得到下圖的結果:
arg-use-double-type.jpg

要注意當使用 Double 型態與 Extended 型態之間互傳後,數值就會有差異
我是雪龍
http://blog.bestlong.idv.tw
http://www.bestlong.idv.tw
‹ 上一主題|下一主題

Archiver|怕失憶論壇

GMT+8, 2024-4-20 17:41 , Processed in 0.027294 second(s), 11 queries .

Powered by Discuz! X1.5

© 2001-2010 Comsenz Inc.