首页 > 其他分享 >踩坑日记:BigDecimal四大坑,真的会用BigDecimal?

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

时间:2023-07-01 11:36:26浏览次数:48  
标签:会用 BigDecimal System equals println new 日记 out


一、前言

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

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

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

前几天写了一下BigDecimal去除多余0返回前端的,大家可以看一下:

Springboot全局配置实现BigDecimal返回前端去除小数点后多余的0

今天就总结一下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("=========不相等就忽略========");
 }

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

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

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

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

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

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

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

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)机制进行处理,这里就不展开看了,感兴趣的可以模拟打断点查看哈!

踩坑日记:BigDecimal四大坑,真的会用BigDecimal?_字符串_03

源码:

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

解决方案就是:使用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);

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

3. BigDecimal精度问题

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

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

BigDecimal bigDecimal2 = new BigDecimal("10");

BigDecimal bigDecimal = new BigDecimal("3");

System.out.println(bigDecimal2.divide(bigDecimal));

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

4. BigDecimal多余0

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

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

如果你想在项目中使用,可以看一下这篇文章:

Springboot全局配置实现BigDecimal返回前端去除小数点后多余的0

BigDecimal bigDecimal1 = new BigDecimal("199.100");
System.out.println(bigDecimal1);
System.out.println(bigDecimal1.stripTrailingZeros());

踩坑日记:BigDecimal四大坑,真的会用BigDecimal?_初始化_06

四、总结

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

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

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


看到这里了,还请动一下您的发财小手,关注一下公众号哈!!谢谢您的关注!!

标签:会用,BigDecimal,System,equals,println,new,日记,out
From: https://blog.51cto.com/wangzhenjun/6598545

相关文章

  • Java 中 BigDecimal 类型的变量的使用
    BigDecimal类的作用:Java的BigDecimal类用于进行高精度的十进制运算,避免了使用浮点数造成的精度丢失问题。 BigDecimal提供了许多方法来执行基本的数学运算,如加减乘除、比较、区域等。 BigDecimal类的常见用法示例:1、创建BigDecimal对象:BigDecimalnumber=......
  • 学习python日记
    python主要是看着小甲鱼的视频开始的变量和字符串1temp=int(input("这次数学考试的成绩:"))23iftemp==100:4print("6")5else:6print("2")7print("4")在编写这个程序时,忘了“:”和int(),一个是格式的错误,还有一个细节上的错误然后记录一下,print函数......
  • 6月26日java&React学习日记
    今日学习java的异常处理,了解了多try,catch,以及单try多catch(需注意子类在上父类在下)异常捕获的方法。同时也学习了React框架,该框架确实比较好用但上手难度较高,需理解其组件化原理,改框架涉及ES6比较深,需加强js或ts的学习。 ......
  • 6月25日java学习日记
    端午节小休息了几天,断开连接了几天,希望快速状态回到吧,今天了解了部分java异常类,Exceptions类为异常类,学习了throw关键字,以及了解了trycatch的用法(基本与C#相同),同时使用HasgMap以及List.of方法实现了斗地主案例。 ......
  • 【nas折腾日记】VoceChat_一款可供私人聊天室的服务
    成品效果展示1、介绍VoceChat是一款支持独立部署的个人云社交媒体聊天服务。17MB的大小在自己的nas上部署,完全可以。而且数据完全由用户自己掌握,传输过程加密。作为个人的平台,可以用于家庭、团队、朋友来使用,与正常使用聊天软件一致,支持文件、图片、语音甚至支持MarkDown语法输......
  • 【Spring面试全家桶】@ComponentScan你真的会用吗
    (文章目录)1.@ComponentScan介绍@ComponentScan是Spring框架提供的一种自动化扫描和加载Bean的机制。它通过指定一个或多个包路径,自动扫描这些路径下的所有类,并将被注解标记的Bean(如@Component、@Service、@Repository、@Controller等等)实例化并加入到Spring容器中。这......
  • BigDecimal
    BigDecimal去一家公司笔试遇到一个这样的问题,问输出结果:BigDecimalbigDeciml1=newBigDecimal(2);BigDecimalbigDeciml2=newBigDecimal(2.1);BigDecimalbigDeciml3=newBigDecimal("2.1");结果:22.1000000000000000888178419700125232338905334472656252.1......
  • Java8 Stream List Map:Stream 流对象汇总 求和 某个属性 BigDecimal MDouble
    测试实体(数字对象使用MDouble):importcom.mchweb.common.lang.MDouble;importlombok.*;@Getter@Setter@Builder(toBuilder=true)@NoArgsConstructor@AllArgsConstructorpublicclassUser{privateMDoublemoney;}importcom.mchweb.common.lang.MDouble;imp......
  • 学习日记——集合
    1.集合框架简介(1)为什么使用集合框架由于数组在创建的时候会默认定义数组的长度,而数组的长度是不变的,导致数组的创建和赋值会导致内存浪费,于是集合作为可以更改长度的特殊的数组,可以解决内存浪费的现象。(2)Java集合框架架构图(3)Java集合框架特点List特点:有序,不唯一(可重复)Se......
  • 学习日记——接口和异常
    1.接口的定义(1)使用接口的原因接口可以实现“多”继承,一个类可以实现多个接口(2)什么是接口1.关键字:interface2.接口中的所有方法都是由publicabstract修饰的3.接口不能实例化4.实现类的关键字是implements必须实现接口的所有方法5.实现类可以实现多个接口(多继承)6.接口中......