2018年8月9日 星期四

[點網]實際的四捨四入

文章連結:https://www.facebook.com/groups/DotNetUserGroupTaiwan/permalink/1945395752420200/

一定要筆記

今天在使用某個成績系統時候遇到四捨五入結果與Excel的ROUND()比對有誤差的問題

與負責的工程師一起測了一下之後

發現造成此問題的原因是因為 .net 中 Math.Round() 函數的行為

於是來與各位分享一下

大家先猜猜下面四捨五入到小數第二位的輸出結果

Math.Round(18.265, 2);

Math.Round(18.265, 2, MidpointRounding.AwayFromZero);

Math.Round(18.275, 2);

Math.Round(18.275, 2, MidpointRounding.AwayFromZero);

Math.Round(18.275M, 2);

Math.Round(18.275M, 2, MidpointRounding.AwayFromZero);

================================

.

.

答案分別是

Math.Round(18.265, 2) = 18.26

Math.Round(18.265, 2, AFZ) = 18.27

Math.Round(18.275, 2) = 18.27

Math.Round(18.275, 2, AFZ) = 18.27

Math.Round(18.275M, 2) = 18.28

Math.Round(18.275M, 2, AFZ) = 18.28

而造成此原因是因Math.Round() 在遇到5的時候

預設是使用 MidpointRounding.ToEven 作為捨去或進位的依據

也就是會round到該位最近的偶數

假設我需要四捨五入到小數第二位

在遇到18.265的時候因為6是偶數,所以會做捨去

如果18.265使用了MidpointRounding.AwayFromZero就會不管奇偶去做四捨五入

得到與Excel的ROUND()相同的18.27

但又有一個問題

18.275出來為何都是18.27?

這是因為在四捨五入過程中遇到了精度丟失

造成了非預期的四捨五入行為

改為decimal之後就正常了

【結論】

處理小數四捨五入的邏輯時

#務必使用decimal型別

#確認四捨五入邏輯

#明確指定MidpointRounding模式

才會得到精確的結果

【補充】

留言中有人有提到.net預設的 ToEven

也就是 Banker's rounding

兩種四捨五入設計給不同的用途

開發前務必確認四捨五入的邏輯

沒有留言:

張貼留言