關於AsFloat(Double型態)計算疑問? |
答題得分者是:maomfh
|
freyasawo
一般會員 發表:5 回覆:6 積分:2 註冊:2008-05-06 發送簡訊給我 |
我使用的是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 發送簡訊給我 |
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 |
freyasawo
一般會員 發表:5 回覆:6 積分:2 註冊:2008-05-06 發送簡訊給我 |
是的 謝謝你的解答 原來是換算位數時產生的問題
不過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 發送簡訊給我 |
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 發送簡訊給我 |
請問你的 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, 註解 無‧
|
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |