Java中BigDecimal的使用和避坑

我们都知道BigDecimal是大精度对象,常用于结算汇金类业务,但实际上这种用法里面也有很多坑。
1、新建BigDecimal时用什么方式?
比如下面这段代码
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = BigDecimal.valueOf(0.01);
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);
输出到控制台的结果是
bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

这是因为二进制只能精确存储小数域是2的负指数幂及其线性组合的数。例如0.25、0.5…
比如
    0.1 = 1/24 + 1/25 + 1/28 +1/29+1/212+1/213…

0.110=0.0001100110011001100110011…2

这是一个二进制无限循环小数。
所以在送给BigDecimal时已经丢失了精度,但是valueof方法的实现是不一样的。
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns ‘0.0’, so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
可以看到实际上是转换成了字符串再做的计算,所以建议避免浮点数的直接运算,可以先都转成字符串。
2、对值的比较
BigDecimal bd1 = new BigDecimal(“1.0”);
BigDecimal bd2 = new BigDecimal(“1.00”);
System.out.println(bd1.equals(bd2));
System.out.println(bd1.compareTo(bd2));

控制台的输出将会是:
false
0

究其原因是,BigDecimal 中 equals 方法的实现会比较两个数字的精度,而 compareTo 方法则只会比较数值的大小。
3、BigDecimal 并不代表无限精度

先看这段代码:
BigDecimal a = new BigDecimal(“1.0”);
BigDecimal b = new BigDecimal(“3.0”);
a.divide(b) // results in the following exception.

结果会抛出异常:
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
如果除法的商是一个无限小数,但我们期望返回精确的结果,那程序就会抛出异常。

回到我们的这个例子,我们需要告诉 JVM 我们不需要返回精确的结果就好了:
BigDecimal a = new BigDecimal(“1.0”);
BigDecimal b = new BigDecimal(“3.0”);
a.divide(b, 2, RoundingMode.HALF_UP)// 0
4、BigDecimal 转回 String 要小心

BigDecimal d = BigDecimal.valueOf(12334535345456700.12345634534534578901);
String out = d.toString(); // Or perform any formatting that needs to be done
System.out.println(out); // 1.23345353454567E+16

可以看到结果已经被转换成了科学计数法,可能这个并不是预期的结果 BigDecimal 有三个方法可以转为相应的字符串类型,切记不要用错:
String toString(); // 有必要时使用科学计数法
String toPlainString(); // 不使用科学计数法
String toEngineeringString(); // 工程计算中经常使用的记录数字的方法,与科学计数法类似,但要求10的幂必须是3的倍数

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值