前言:
- 在计算有小数的参数中,float 、double 会有丢失精度的可能性,因此对于精度比较敏感的参数,例如金钱之类的常使用BigDecimal。
- 看一下下面的例子:
public static void main(String[] args) {
Float f1 = 100.12f;
Float f2 = 30.03f;
System.out.println("f1 - f2 = " + (f1 - f2));
Double d1 = 100.12;
Double d2 = 30.04;
System.out.println("d1 - d2 = " + (d1 - d2));
}
打印结果:
f1 - f2 = 70.090004
d1 - d2 = 70.08000000000001
- 解释: float和double都是浮点数, 通过二进制保存数据,由于小数转化为二进制时,会存在无线循环的二进制,但是float和double有精度范围,因此在小数的计算中往往会出现结果不可预判,当在计算金额必须是完全精确的计算, 故不能使用double或者float, 而应该采用java.math.BigDecimal
BigDecimal 的加减乘除:
- 加减乘除方法分别是 add、subtract、multiply、divide
- 除法需要定义保留的小数位和规则,否则当计算结果无限时,可能会报错。
public static void main(String[] args) {
// 须使用String类型的字符串,如果直接使用 100.12,会认为float类型,计算之前就已经丢失精度
BigDecimal big1 = new BigDecimal("100.12");
BigDecimal big2 = new BigDecimal("30.03");
// 加法: 130.15
System.out.println(big1.add(big2));
// 减法:70.09
System.out.println(big1.subtract(big2));
// 乘法:3006.6036
System.out.println(big1.multiply(big2));
// 除法,保留两位小数: 3.33
System.out.println(big1.divide(big2,2,BigDecimal.ROUND_DOWN));
}
BigDecimal 的小数的取舍:
-
较为常用的有:
BigDecimal.ROUND_DOWN: 丢弃多余的小数。
BigDecimal.ROUND_UP: 多余的小数大约 0 则进位。
BigDecimal.ROUND_HALF_DOWN : 四舍五入, 剩余的小数 <= 5 则丢弃
BigDecimal.ROUND_HALF_UP: 四舍五入, 剩余的小数 >= 5 则进位
- 参数可以用于 setScale 和 除法 需要定义保存的小数位。
代码示例:
public static void main(String[] args) {
/**
* 规则: 保留两位小数,多余的小数舍弃
* 分析:无
* 结果: 100.12
*/
System.out.println(new BigDecimal("100.12101").setScale(2,BigDecimal.ROUND_DOWN));
/**
* 规则: 保留两位小数,其他小数 >0则进位
* 分析: 保留两位小数后,剩余小数 1.01 > 0 所以进位
* 结果: 100.13
*/
System.out.println(new BigDecimal("100.12101").setScale(2,BigDecimal.ROUND_UP));
/**
* ROUND_HALF_DOWN 情况一:
* 规则: 保留两位小数,其他小数 <=5 则舍弃
* 分析:保留两位小数后,剩余小数 5.0 = 5 所以舍弃
* 结果:100.12
*/
System.out.println(new BigDecimal("100.1250").setScale(2,BigDecimal.ROUND_HALF_DOWN));
/**
* ROUND_HALF_DOWN 情况二:
* 分析:保留两位小数后,剩余小数 5.1 > 5 所以进位
* 结果:100.13
*/
System.out.println(new BigDecimal("100.1251").setScale(2,BigDecimal.ROUND_HALF_DOWN));
/**
* 规则:保留两位小数,其他小数 >=5 进位
* 分析:保留两位小数后,剩余小数 5.0 >= 5 所以进位
* 结果:100.13
*/
System.out.println(new BigDecimal("100.1250").setScale(2,BigDecimal.ROUND_HALF_UP));
}
BigDecimal 比较大小:
BigDecimal 比较通过 compareTo 来比较大小,
结果: 0 相等、 1 前者大于后者、-1 前者小于后者
例:
public static void main(String[] args) throws ParseException {
BigDecimal big = new BigDecimal(100.13);
BigDecimal small = new BigDecimal(50.13);
// 等于 0
System.out.println(big.compareTo(big));
// 大于 1
System.out.println(big.compareTo(small));
// 小于 -1
System.out.println(small.compareTo(big));
}
BigDecimal 格式化:
一般使用 DecimalFormat,参数类型为格式化后的类型,简单例子:
public class Test {
public static void main(String[] args) throws Exception{
DecimalFormat df = new DecimalFormat("#0.00");
BigDecimal bg = new BigDecimal(1.2353);
System.out.println(df.format(bg));
System.out.println(df.format(1.2353));
}
}
打印结果:
小结:
好处: BigDecimal 处理数据可以根据自己的需要获得准确的值
缺点:BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显,因根据实际需求决定使用哪种类型。