踩坑日记:BigDecimal四大坑,真的会用BigDecimal?

小王博客基地   2023-06-30 09:13:21

一、前言

最近在项目中使用BigDecimal存储订单的数量,数据库保留三位小数。需求是数量变化了就会有其他操作,头脑发热顺手写了个equals进行判断是不是相等!

后来怎么测都是不相等!百思不得其解,看了一下equals方法才知道!

BigDecimal值的比较官方推荐是compareTo的,如果数据库没有保留小数,用equals是没问题,但是不建议,非常不建议!!


(资料图)

今天就总结一下BigDecimal使用时需要注意的点!

二、BigDecimal在理解

BigDecimal是Java编程语言中的一个类,属于java.math包,用于进行高精度的十进制数计算。它提供了对任意精度的十进制数进行精确计算的能力,适用于需要保持精度和执行准确计算的场景。

与基本的浮点数类型(如float和double)不同,BigDecimal使用基于整数的表示方法,通过存储和处理数值的每一位来避免精度丢失。这使得它可以表示极大或极小的数字,并执行准确的计算。

BigDecimal在金融领域、货币计算、税务计算、精确计算需求以及其他需要保持精度和执行准确计算的场景中广泛应用。

「当然要注意」:

BigDecimal对象是不可变的,这意味着一旦创建就不能修改其值。每个操作都会产生一个新的BigDecimal对象作为结果。

由于BigDecimal是一个对象,并且执行计算时需要更多的内存和处理时间,与使用原生数据类型相比,它可能会稍微降低性能。因此,在大量计算或对性能要求较高的情况下,需要权衡使用BigDecimal的优势和劣势。

三、BigDecimal注意点1、BigDecimal使用equals

这就是小编最近需要的,我们还是要提高自己的编码规范哈,不要学小编,equals用习惯了,看见比较就用!

当然也不用使用== !=来比较哈!!

我们来个例子感受一下哈!

BigDecimal dbNum = new BigDecimal("2.000");BigDecimal num = new BigDecimal("2"); if (dbNum.equals(num)) {     System.out.println("=========相等我就操作========"); }else {     System.out.println("=========不相等就忽略========"); } BigDecimal dbNum1 = new BigDecimal("2"); if (dbNum1.equals(num)) {     System.out.println("=========相等我就操作========"); }else {     System.out.println("=========不相等就忽略========"); }

我们从源码来看一下这个equals内部到底是怎么比较的:

我们看到BigDecimal里重写了equals方法!

前面简单的就不说什么意思了,我们挑重点说一下:

scale != xDec.scale:这是比较两个数的精度长度是否相等,长度不一致直接返回false,这就是我们例子返回false的原因!

我们打断点可以看到一个是3位精度,一个0位!

long s = this.intCompact; long xs = xDec.intCompact;:这俩放一起说:

表示 BigDecimal 对象的紧凑表示形式,这个又分为jdk8之前和之后

在 JDK 1.8 之前的版本中,BigDecimal 内部使用一个 int 数组来表示大整数。每个元素都代表了 BigDecimal 的一部分位数。这种表示方式需要额外的内存空间,并且对于小数和较小的整数来说是不必要的。

为了优化性能和节省内存,JDK 1.8 引入了 intCompact 属性,它将 BigDecimal 内部的表示形式转换为一个long值。这个 long 值可以直接存储整数值,而对于较大的数字,则使用溢出(overflow)和膨胀(inflation)机制进行处理。

具体而言,当 BigDecimal 对象的值可以用 long 类型表示时,intCompact 将存储该长整型值。如果值超过 long 类型的范围,则会使用其他方式进行存储,例如使用 intVal 字段来存储 int 数组。

为了形象,我们把第二次比较的两个数都变为:2.0,经过intCompact后,变为20来进行后续操作! 如果超过Long的最大值就会:使用溢出(overflow)和膨胀(inflation)机制进行处理,这里就不展开看了,感兴趣的可以模拟打断点查看哈!

源码:

@Overridepublic 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());}

解决方案就是:使用compareTo,compareTo方法实现了Comparable接口,准备的比较的两者! 有兴趣可以debug看看compareTo方法!这里就不给大家展示了!!

2、BigDecimal初始化

这个基本上大家都会注意,用字符串或整数初始化:为避免浮点数转换引起的精度丢失,最好使用字符串或整数来初始化BigDecimal对象!double、float类型只能保留有限的有效数字,分别是15个左右7、8个,我们写个例子就明白了!

我们写上IDEA都看不下去要提示你可以优化,Alt+Enter让IDEA来解决吧!!

BigDecimal bigDecimal2 = new BigDecimal("0.11");BigDecimal bigDecimal = new BigDecimal(0.11);System.out.println(bigDecimal);System.out.println(bigDecimal2);
3、BigDecimal精度问题

我们在使用BigDecimal 进行计算的时候,一定要保留小数,基本上所有的计算需求都会让你保留几位小数。没有的话得到无限小数就会报错异常:ArithmeticException!

保留小数的规则这里就不展开说了,大家根据自己需要去看api就可以了!

BigDecimal bigDecimal2 = new BigDecimal("10");BigDecimal bigDecimal = new BigDecimal("3");System.out.println(bigDecimal2.divide(bigDecimal));
4、BigDecimal多余0

这个就是前面最开始说的,我们保留的位数很多,有的前端展示又不想看到!这时就要把多余的0去掉!

这其实不算坑了,这算是优化显示哈!

BigDecimal bigDecimal1 = new BigDecimal("199.100");System.out.println(bigDecimal1);System.out.println(bigDecimal1.stripTrailingZeros());
四、总结

我们来在总结有哪些注意事项哈:

BigDecimal比较大小的时候要使用compareTo();BigDecimal用字符串或整数初始化;BigDecimal计算时尽量指定保留精度位数;按需去除多余0;BigDecimal都是不可变的;

大家一定注意这些东西,特别是设计到钱的计算,一个不小心一个小目标没了!

最新供应

更多