四舍五入是財務類應用中常見的需求,按中國人的財務習慣,遇到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的條件。
當然,這是權宜之計,如果大家有更好的通用方法,歡迎指正。