解決算術式四捨五入(SimpleRoundTo 有臭蟲) |
|
cancer
高階會員 發表:58 回覆:319 積分:190 註冊:2004-07-31 發送簡訊給我 |
各位好,今天下午搞 SimpleRoundTo() 搞了半天,原來它是有 Bug 的,用 FormatFloat() 一樣不行,
// 傳入立即值 SimpleRoundTo(1213.245, -2) // 傳回 1213.25,OK FormatFloat('#0.00', 1213.245,) // 傳回 "1213.25",也OK // 先放變數 f var f : double; f := 1213.245; SimpleRoundTo(f, -2) // 傳回 1213.24,吐血了,明明都是 1213.245 跟上面一樣! FormatFloat('#0.00', f) // 傳回 "1213.24",也吐血 最後只好自己寫一固函式來處理 uses Math; function ArithmeticRoundTo(Value : double; Digit : integer) : double; var txt : string; begin Digit := Abs(Digit); //不接受負數 txt := FormatFloat('#0.' DupeString('0', Digit 1), Value); // 例如 2 位時,用第 3 位來判斷,所以 Digit 1 if txt[Length(txt)] = '5' then // 最後一位遇 5 就變 9,則 SimpleRoundTo()一定會進位 txt[Length(txt)] := '9'; Result := SimpleRoundTo(StrToFloat(txt), -Digit); end; |
tobylin
一般會員 發表:1 回覆:15 積分:18 註冊:2009-12-25 發送簡訊給我 |
|
P.D.
版主 發表:603 回覆:4038 積分:3874 註冊:2006-10-31 發送簡訊給我 |
其實你去查formatfloat() 的code
function FormatFloat(const Format: string; Value: Extended): string; var Buffer: array[0..255] of Char; begin if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong); SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvExtended, PChar(Format))); end; 發現了嗎? f 應該宣告為 extended , 而不是 double, 改過來就ok了 2.另外, 如果 f 宣告為 double, 你試試看 f:= 1213.2451 及 f:= 1213.2450 所得到的結果? 我沒有很仔細去研究 double 的結構, 或許這當中 extended 轉換成 double 時已經是轉過一次了
編輯記錄
P.D. 重新編輯於 2011-01-13 09:38:58, 註解 無‧
|
cancer
高階會員 發表:58 回覆:319 積分:190 註冊:2004-07-31 發送簡訊給我 |
|
cancer
高階會員 發表:58 回覆:319 積分:190 註冊:2004-07-31 發送簡訊給我 |
|
cancer
高階會員 發表:58 回覆:319 積分:190 註冊:2004-07-31 發送簡訊給我 |
===================引 用 P.D. 文 章=================== 發現了嗎?? f 應該宣告為 extended , 而不是? double, 改過來就ok了 2.另外, 如果 f 宣告為 double, 你試試看 f:= 1213.2451 及 f:= 1213.2450 所得到的結果? 我沒有很仔細去研究 double 的結構, 或許這當中 extended 轉換成 double 時已經是轉過一次了 您好,我想是傳入 1213.245 結 SimpleRoundTo(),存入 extended 後變成 1213.24999 之類的,第三位由原本的 5 變成 4,所以才無法進位,但我最納悶的是傳入立即值 1213.245 卻可以進位,一樣是存入 extended,但又是正常的 。 |
P.D.
版主 發表:603 回覆:4038 積分:3874 註冊:2006-10-31 發送簡訊給我 |
看不懂唉! 沒有人要你改formatfloat()啊! 我提到的 formatfloat() 內建是宣告 extended 的值, 而不是double,
所以當你宣告double 時, 其實透過 formatfloat 已經被轉過extended 了, 而這當中Delphi的轉換是否有進位問題就不知道了, 所以只要你 f 宣告為 extended 就可以達成 上述的結果! 這是我實測的結果 ===================引 用 cancer 文 章=================== FormatFloat() 是 Delphi 內建函式,我不想修改它,理由是: 1.重腦重灌要重裝 Delphi 2.程式可能會拿到更高版次的 Delphi?中重新編譯 3.程式可能會被其他同事拿來重新編譯 4.我可能把公司程式帶回家用筆記型電腦編譯 以上情形(尢其第 3 點),只要忘記修改 FormatFloat() 的函式定義 ,Build 出來程式,有錯查不出來,只能等客戶反應才會知道,有時候還被客戶取笑說 "連四捨五入那麻簡單的東西還會弄錯!"。 |
cancer
高階會員 發表:58 回覆:319 積分:190 註冊:2004-07-31 發送簡訊給我 |
|
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/
編輯記錄
GrandRURU 重新編輯於 2015-05-19 15:49:02, 註解 無‧
|
wiseinfo
一般會員 發表:3 回覆:4 積分:1 註冊:2006-07-13 發送簡訊給我 |
看Math中的SimpleRoundTo有3个重载, 当传入参数是Double就有问题.
直接SimpleRoundTo(1213.245, -2) 会是OK的, 跟踪会进入Extended的重载,当Extended处理了. 我的解决办法, 这就是DELPHI折腾人的地方 function RoundEx(vValue: Extended; vScale: Integer): Extended; var EValue: Extended; begin EValue := StrToFloat(FloatToStr(vValue)); //这是必须的;FormatFloat也同样必须这样才正确 Result := SimpleRoundTo(EValue, vScale); end; StrToFloat返回的是Extended,应该一句可以搞定 Result := SimpleRoundTo(StrToFloat(FloatToStr(vValue)), vScale); |
GrandRURU
站務副站長 發表:240 回覆:1680 積分:1874 註冊:2005-06-21 發送簡訊給我 |
|
aftcast
站務副站長 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
這種改了也不會增加收入的bug,我看到 xe20可能也不會好。而且許多的delphier都自幹這個問題了…就參考前人做法吧。喔,對了,我是沒裝 xe8 ,但我不相信解決了。這種陳年的bug,在c++ builder更是多,report n 次也沒得到回應 (是啦,delphi都沒處理了,cb要叫什麼? )。
pd大 有用 xe8,看看他有沒有時間幫你測… > < ;-) ===================引 用 GrandRURU 文 章=================== SimpleRoundTo with Double 在 XE7 中 Bug 還在 好像是 Trunc 組合語言出錯 不知道 XE8 修正了沒?
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan |
GrandRURU
站務副站長 發表:240 回覆:1680 積分:1874 註冊:2005-06-21 發送簡訊給我 |
目前已知 Double 型別會有精度問題
但如果一開始就宣告 Extended,精度問題就自然而然地排除了 如果 Double 想排除所謂精度的 "Bug" 可以按 wiseinfo 大的寫法 [code Delphi] function RoundEx(vValue: Double; vScale: Integer): Extended; var EValue: Extended; begin EValue := StrToFloat(FloatToStr(vValue)); //这是必须的;FormatFloat也同样必须这样才正确 Result := SimpleRoundTo(EValue, vScale); end; [/code] 如果有需要額外做 Extended 可以再使用 overload 重載函式 以上 |
Main Chen
高階會員 發表:29 回覆:135 積分:127 註冊:2002-10-07 發送簡訊給我 |
|
it1506
初階會員 發表:32 回覆:89 積分:49 註冊:2011-02-16 發送簡訊給我 |
|
P.D.
版主 發表:603 回覆:4038 積分:3874 註冊:2006-10-31 發送簡訊給我 |
我自已由久以來的做法, 如果要得到精準的進位, 自己寫進位公式,
通常我就是把原值 * 10^n 放大(看你要取決到小數幾位), 然後 5, 再回除10^n, 到目前為止並沒有發生進位的問題 例如 10.449 希望得到答案是 10.45 10.449 * 10^3 = 10449 5 = 10454 / 10 ^ 3 = 10.454, 改成 string "10.454", 取回 "10.45" 再換回數值 = 10.45 ===================引 用 aftcast 文 章=================== 這種改了也不會增加收入的bug,我看到 xe20可能也不會好。而且許多的delphier都自幹這個問題了…就參考前人做法吧。喔,對了,我是沒裝 xe8 ,但我不相信解決了。這種陳年的bug,在c builder更是多,report n 次也沒得到回應 (是啦,delphi都沒處理了,cb要叫什麼? )。 pd大 有用 xe8,看看他有沒有時間幫你測… > < ;-) 不知道 XE8 修正了沒? |
it1506
初階會員 發表:32 回覆:89 積分:49 註冊:2011-02-16 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |