線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:3241
推到 Plurk!
推到 Facebook!

關於AsFloat(Double型態)計算疑問?

答題得分者是:maomfh
freyasawo
一般會員


發表:5
回覆:6
積分:2
註冊:2008-05-06

發送簡訊給我
#1 引用回覆 回覆 發表時間:2008-08-12 17:21:11 IP:61.221.xxx.xxx 訂閱
我使用的是Delphi 5

只是用一個很單純的程式下去跑,就發現AsFloat的小數位累加算法有問題,而AsFloat是以Double為計算單位

後來寫了個簡易小程式測試,程式碼如下:


[code delphi]
procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
vA : Double;
begin
vA := 1.01;
for i:= 1 to 2000 do
begin
vA := vA 0.01;
if i mod 500 = 0 then
begin
if vA = (1.01 i*0.01) then
showmessage(floattostr(vA) '= (1.01 '
IntToStr(i) '*0.01)')
else
showmessage('迴圈數為500的倍數');
end;

if (i - 9) mod 100 = 0 then
if (vA * 10) - Round(vA * 10) = 0 then
showmessage('(vA*10) - Round(vA*10) = 零');
end;
end;
[/code]

他只會秀『showmessage('迴圈數為500的倍數');』
用debug中斷,以F7的ADD Watch設為Floating point
發現應為6.01的值會變為 6.00999999999991541

這是只有Delphi 5才會發生的問題嗎?

我用Extended及Double,做if 兩邊相等的判斷都會出錯,Currency下去計算會有一點問題

這種奇怪的問題要怎麼救呢? 囧
maomfh
初階會員


發表:3
回覆:10
積分:27
註冊:2008-01-05

發送簡訊給我
#2 引用回覆 回覆 發表時間:2008-08-14 14:36:17 IP:218.163.xxx.xxx 訂閱
1) 實數型別是利用浮點表示法來表達一個數字,而電腦中的浮點表示法是用2進位來表示的, 所以有時就會發生 10進位的一個有理小數, 在轉成 2進位時卻會變成一個無窮小數, 例如 0.1 這個數,在轉成 2 進位小數時會變成 0.00011001100  後面的1100一直循環, 所以用浮點數來表示10進位的小數就有可能會有誤差產生. 這一點你要明白.

2) 程式中的 vA := vA 0.01 , 其中 0.01 加了 N 次, 則其精度也在一次次的加法中喪失, 運算愈多次,誤差值就會愈大, 而 1.01 N*0.01 因只運算一次,所以數值會幾乎等於原值, 這是 vA為什麼會不等於 1.01 N*0.01 的原因,

3) 可以改成 if vA - (1.01 i*0.01) < 0.01 then 這樣結果就會正確了. 但如果其中的 i 大到某個程度, 整個算式的誤差值可能會超過 0.01, 所以以上算式要成立則 i < 1,882,906,226
------
Maomfh
編輯記錄
maomfh 重新編輯於 2008-08-14 14:40:52, 註解 無‧
maomfh 重新編輯於 2008-08-14 14:57:03, 註解 無‧
freyasawo
一般會員


發表:5
回覆:6
積分:2
註冊:2008-05-06

發送簡訊給我
#3 引用回覆 回覆 發表時間:2008-08-16 08:38:52 IP:61.221.xxx.xxx 訂閱
是的  謝謝你的解答  原來是換算位數時產生的問題

不過0.01是我舉例的例子 實際上他是跑 會計的金額計算 所以我無法去掌控他的誤差值

因為你在比較借貸方的時候,可能多個借方及多個貸方會加總起來做比對,此時就有可能會有誤差值的產生

從數學上看可能是 12345.65
但因借貸兩方數目的加總下一邊會是12345.650000000000000002
另一邊可能是12345.650098888888888881
因此借貸會比較不平衡
但Currency可以把金額調整的比較正確,但我比較好奇的是Double除了去設定取小數位的值以外,還有其他的解決方法嗎?


===================引 用 maomfh 文 章===================
1) 實數型別是利用浮點表示法來表達一個數字,而電腦中的浮點表示法是用2進位來表示的, 所以有時就會發生 10進位的一個有理小數, 在轉成 2進位時卻會變成一個無窮小數, 例如 0.1 這個數,在轉成 2 進位小數時會變成 0.00011001100 後面的1100一直循環, 所以用浮點數來表示10進位的小數就有可能會有誤差產生. 這一點你要明白.

2) 程式中的 vA := vA 0.01 , 其中 0.01 加了 N 次, 則其精度也在一次次的加法中喪失, 運算愈多次,誤差值就會愈大, 而 1.01 N*0.01 因只運算一次,所以數值會幾乎等於原值, 這是 vA為什麼會不等於 1.01 N*0.01 的原因,

3) 可以改成 if vA - (1.01 i*0.01) < 0.01 then 這樣結果就會正確了. 但如果其中的 i 大到某個程度, 整個算式的誤差值可能會超過 0.01, 所以以上算式要成立則 i < 1,882,906,226
maomfh
初階會員


發表:3
回覆:10
積分:27
註冊:2008-01-05

發送簡訊給我
#4 引用回覆 回覆 發表時間:2008-08-16 21:03:47 IP:218.163.xxx.xxx 訂閱
1) Currency並不會把小數轉換成2進位的小數, 它只固定取小數4位,且把整個數值乘以10,000,再作數值運算,運算完成後又將數值除10,000,所以它沒有精度誤差的問題. 但它只能有4位小數.
2) Double這種浮點數型態,拿來表示 小數 就一定有精度的問題, 如果一定要用在借貸兩方都用 double來運算, 那可以將運算後的双方結果再用 RoundTo 或 SimpleRoundTo 函數來修正, 這樣兩個數就可拿來比較了,或更精準一點在每次運算後都用RoundTo 或 SimpleRoundTo修正.然後再做下一筆運算.


------
Maomfh
編輯記錄
maomfh 重新編輯於 2008-08-16 21:22:37, 註解 無‧
freyasawo
一般會員


發表:5
回覆:6
積分:2
註冊:2008-05-06

發送簡訊給我
#5 引用回覆 回覆 發表時間:2008-08-18 09:56:42 IP:61.221.xxx.xxx 訂閱
請問你的 RoundTo 或 SimpleRoundTo 函數,是從哪來的?
我用F1查不到

後來我終於知道最正宗的解決方法了…
就是一開始在BDE設定的地方把ENABLE BCD把它Turn TURE
這樣從後端資料庫撈來的資料會用BDE去做精算動作
就可以避免Extended及Double的運算問題了。

突然想到…設BDE這樣好像還是只能解決資料庫運算的問題,但單純的Double及Extended運算還是沒解決..

===================引 用 maomfh 文 章===================
1) Currency並不會把小數轉換成2進位的小數, 它只固定取小數4位,且把整個數值乘以10,000,再作數值運算,運算完成後又將數值除10,000,所以它沒有精度誤差的問題. 但它只能有4位小數.
2) Double這種浮點數型態,拿來表示 小數 就一定有精度的問題, 如果一定要用在借貸兩方都用 double來運算, 那可以將運算後的双方結果再用 RoundTo 或 SimpleRoundTo 函數來修正, 這樣兩個數就可拿來比較了,或更精準一點在每次運算後都用RoundTo 或 SimpleRoundTo修正.然後再做下一筆運算.


編輯記錄
freyasawo 重新編輯於 2008-08-18 10:06:09, 註解 無‧
系統時間:2024-05-03 9:09:53
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!