存在的問題
public class Client{
public static void main(String[] args){
System.out.print(10.00-9.60);
}
}
我們期望的結果爲0.4,但是打印的結果卻是0.40000000000000036
原因:機器運算時二進制運算,將0.4轉爲二進制,他是無限循環小數,可以這樣理解在十進制世界裏沒有辦法準確表示1/3,這樣一來結果很容易出現偏差
嘗試解決方案:
publi class client{
public static void main(String[] args){
NumberFormat f=new DecimalFormat("#.##");
Syso(f.format(10.00-9.60));
}
}
結果:0.4
看似解決了,但是隱藏了一個很深的問題,一般金融會計系統會記錄小數點後四位,
但是在彙總,展現,報表中則只記錄小數點的2位小數,如果使用浮點數來計算貨幣,
在大批量的加減乘除後結果會有很大的差距!會計系統要的就是準確,但是卻因爲計算機的緣故不準確了
真正解決方案:
1 使用BigDecimal
專門用來彌補浮點數無法精確計算的缺憾而設計的類,並且本身也提供了加減乘除的常用數據算法.
特別是與數據庫Decimal類型的字段映射時,BigDecimal時最優的結解決方案
金額的運算(小數的精確計算)
BigDecimal b1 = new BigDecimal("0.03");
BigDecimal b2 = new BigDecimal("0.04");
// 加法
System.out.println(b1.add(b2));
// 減法
System.out.println(b2.subtract(b1));
// 乘法
System.out.println(b2.multiply(b1));
// 除法
// 如果 BigDecimal 是正的,則做 ROUND_UP 操作;如果爲負,則做 ROUND_DOWN 操作。
System.out.println(b2.divide(b1, RoundingMode.HALF_UP));
注:在這裏HALF_UP是一種舍入模式
BigDecimal和RoundingMode是一個絕配,想要採用什麼舍入模式
使用RoundingModel設置即可,目前java支持七種舍入方式:
1 ROUND_UP:遠離零方向舍入
也就是說,想絕對值最大限額方向舍入,只要捨棄位非0即進位
2 ROUND_DOWN:遠離零方向舍入
也就是說,想絕對值最小的方向輸入,注意:所有的位都捨棄,不存在進位情況
3 ROUND_CEILING:向正無窮方向舍入
如果是正數,舍入行爲類似ROUND_UP;
如果是負數,舍入行爲類似於ROUND_DOWN;
注意:Math.round方法使用的即爲此模式
4 ROUND_FLOOR:向負無窮方向舍入.
如果是正數,則舍入行爲類似於ROUND_DOWN;
如果爲負數,則舍入行爲 類似於ROUND_UP;
5 HALF_UP:最近數字舍入(5進) 最經典的四捨五入
6 HALF_EVEN:銀行家算法
規定:
四捨六入五考慮,五後非零就進一,五後爲0看奇偶,五前爲偶應捨去,
五前爲奇要進一
2 使用整型
把參數與運算的值擴大100倍,並轉爲整形,然後在展現時,再縮小100倍,
這樣處理的好處是計算簡單,準確,但只適用於一般在非金融行業應用較多如POS機