存在的问题
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机