惡心的0.5四舍五入問題


四舍五入是財務類應用中常見的需求,按中國人的財務習慣,遇到0.5統一向上進位,但是c#與java中默認的卻不是這樣。

見c#代碼:

1         static void Main(string[] args)
2         {
3             Decimal d = 301353.05M;
4             Console.WriteLine(d);//301353.05
5             Console.WriteLine(Math.Round(d, 1));//301353.0
6             Console.WriteLine(Math.Round(d, 1, MidpointRounding.AwayFromZero));//301353.1
7 
8             Console.ReadKey();
9         }

默認情況下,如果要舍棄的位置上,正好值是5,系統會看前一位是奇數還是偶數,如果是偶數,則丟棄最后1位,即上面代碼行5,輸出的結果為 301353.0,這不符合國人的習慣,所以要人為指定第3個參數"MidpointRounding.AwayFromZero"

 

java中也提出了類似的做法,但是有“缺陷”

1     @Test
2     public void testScale(){
3         double d = 301353.05;
4         BigDecimal decimal = new BigDecimal(d);
5         System.out.println(decimal);//301353.0499999999883584678173065185546875
6         System.out.println(decimal.setScale(1, RoundingMode.HALF_UP));//301353.0
7     }

類似的,在設置精度時,可以指定一個額外的參數RoundingMode.HALF_UP,表示如果要舍棄的這一位正好是5,則向上進位,代碼看似沒有問題,但是輸出值卻是301353.0

原因在於BigDecimal在計算機內部的存儲值為"301353.0499999999883584678173065185546875",即小數點第2位是4,上面的代碼要求精度到1位,所以代碼執行時,只看第2個小數位,其值為4,沒有到HALF的標准,因此直接扔掉

 

改進方法:

1     @Test
2     public void testScale(){
3         double d = 301353.05 + 0.0000000001;
4         BigDecimal decimal = new BigDecimal(d);
5         System.out.println(decimal);//301353.0500000001047737896442413330078125
6         System.out.println(decimal.setScale(1, RoundingMode.HALF_UP));//301353.1
7     }

在滿足財務精度的前提下,將要處理的數字加1個微小的偏移量,這樣計算機內部存儲時,值變成301353.0500000001047737896442413330078125,這樣小數位第2位變成了5,滿足了HALF_UP的條件。

 

當然,這是權宜之計,如果大家有更好的通用方法,歡迎指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM