时间: 2024-01-20 【学无止境】 阅读量:共360人围观
简介 BigDecimal常用于需要精确计算数据的场景中,例如涉及到订单金额和银行账务数据等场景。
1. 使用BigDecimal的构造函数传入浮点数
其实这个问题我们在使用Float、Double等浮点类型进行计算时,也会经常遇到,比如说下面这个代码
@Test public void bigDecimalDemo1(){ float floati =1; float float2 = 0.9f; System.out.println(float1 - float2) }
输出结果是多少呢?0.1?不是,输出结果是0.100000024。因为 0.9 无法被精确表示为有限位数的二进制小数。在转换为二进制时可能会产生近似值。因此,在进行减法运算时,实际上是对近似值进行计算,而不是对准确的 0.9 进行计算。这导致了精度丢失,最终的计算结果也是一个近似值。因此,输出结果不是准确的 0.1,而是一个近似值。
小伙伴肯定能想到使用BiqDecimal来避免这个问题,这时候第一个需要避免的陷阱就来了。看以下代码:
@Test public void bigDecimalDemo2(){ BigDecimol bigDecimal1 = new BigDecimal(8.81): BigDecimal bigDecimal2 = BigDecimal.value0f(e.81); System.out,printin("bigDecimal1:" + bigDecimal1); System.out.println("bigDecimal2:" + bigDecimat2); }
输出结果如下:
bigDecimal1 =0.01000000000000000020816681711721685132943093776702880859375
bigDecimal2 =0.01
观察输出结果我们可以知道,使用BigDecimal时同样会有精度的问题。所以我们在创建BigDecimal对象时,有初始值使用BigDecimal.valueOf0)的方式,可以避免出现精度问题。
为什么会出现差异?
在使用new BigDecimal()实际上是将 ú.01转换为二进制近似值,并将其存储为 BigDecimal 对象。因此,结果中存在微小的误差,即输出结果为0.01000000000000000020816681711721685132943093776702880859375。
而BigDecimal.valueOf()不同,其内部是先将double转为String,因此不存在精度问题。
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)); }
TIPS:
2. 使用equals()方法进行数值比较
日常项目我们是如何进行BigDecimal数值比较呢?使用equals方法还是compareTo方法?如果使用的是equals方法,那就需要注意啦。看一下示例:
@Test public void bigDecimalDemo3(){ BigDecimal bigDecimal1 = new BigDecimal("0.01"); BigDecimal bigDecimal2 = new BigDecimal("0.010"); System.out.println(bigDecimal1.equals(bigDecimal2)); System.out.println(bigDecimal1.compareTo(bigDecimal2)); }
输出结果如下:
false
0
观察结果可以知道使用equals比较结果是不相等的;compareTo的结果为0代表两个数相等;·compareTo实现了Comparable接口,比较的是值的大小,返回的值为-1-小于,0-等于,1-大于。
为什么equals返回的是false?
public boolean equals(object x){ if(!(x instanceof BigDecimal)) return false; BigDecimal xDec = (BigDecimal) x; if(x== this) return true; if(scale != xDec.scale) return false; long s= this.intcompact; long xs= xDec.intcompact; if (S != INFLATED) { if(xS= INFLATED) xs = compactValFor(xDec.intVal); return xs == s; } else if (xs ! INFLATED) return xs == compactValFor(this.intVal); return this.inflated().equals(xDec.inflated()); }
我们观察equals的实现逻辑可以知道,BigDecimal重写了equals方法,重写后的关键代码:
if (scale != xDec.scale)
return false;
也就是会比较两个数值的精度,精度不同返回false。
3. 使用不正确的舍入模式
使用BigDecimal进行运算时,一定要正确的使用舍入模式,避免舍入误差引起的问题,并且有时候出现结果是无限小数,程序会抛出异常,比如说:
@Test public void bigDecimalDemo4(){ BigDecimal bigDecimal1 = new BigDecimal("1.00"); BigDecimal bigDecimal2 = new BigDecimal("3.00"); BigDecimal bigDecimal3 = bigDecimall.divide(bigDecimal2); System.out.printin(bigDecimal3); }
输出结果如下:
java.lang.ArithmeticException: Non-terminating decimal expansion;no exact representable decimal result
简单的来说,如果在除法运算过程中,其结果是一个无限小数,而操作的结果预期是一个精确的数字,那么将会抛出ArithmeticException异常,
此时,我们只要正确指定结果精度即可:
@Test public void bigDecimalDemo4(){ BigDecimal bigDecimal1 = new BigDecimal("1.0e"); BigDecimal bigDecimal2 = new BigDecimal("3.00"); BigDecimal bigDecimal3 = bigDecimall.divide(bigDecimal2,2,RoundingMode.HALF_UP); System.out.printin(bigDecimaL3); }
输出结果如下:
0.33
TIPS: