你看,ChatGPT都知道优先使用BigDecimal

导读:本篇文章讲解 你看,ChatGPT都知道优先使用BigDecimal,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

在这里插入图片描述

不是三婶儿偏执,非要吐槽。家人们,咱就是说,按照基操逻辑谁会把严格金额计算相关的数据使用double类型呢…
“我以为吕布已经够勇猛了,这是谁的部下?”

前几天,一同事让帮忙写段代码。内容比较常规,就是按照自定义规则自动计算出一些金额数据。楼主想着暂时也不忙,就帮着写写呗。好家伙!不写不知道,当看到他用Double类型定义存储金额时,内心瞬间沸腾了。嗯,真是个小(大)可(傻)爱(逼)…

在这里插入图片描述

咱就是说,金额相关计算第一考虑肯定是确保精准,优选BigDecimal类型呀,Double类型很容易丢失精度的。尤其是金额,一定要严谨!

你看chatgpt都知道优先使用BigDecimal。

在这里插入图片描述

果不其然,这…真是暴风雨的前奏。我发现有好几个业务模块都使用了这些金额数据做运算,多次加减乘除之类的…

好家伙,“海燕啊,你可长点心吧!”。

在这里插入图片描述

那下面楼主就来详细分析一下,为什么更加建议使用BigDecimal。开整!

一、BigDecimal类型数据和Double类型

首先,先来了解一下什么是BigDecimal、什么是Double、什么是double。以及Double和double之间有什么关系。

嗯,是有关系,但也不是情侣关系…

在这里插入图片描述

1.1、BigDecimal

对于什么是BigDecimal,百度百科中这样描述:

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

主要是: 可对超过16位有效位的数进行精确的运算。

1.2、Double和double的区别

1.2.1、double

八大基本数据类型之一,双精度浮点数。

double(双精度浮点型)是计算机使用的一种资料型别。比起单精度浮点数(float),double(双精度浮点数)使用 64 位(8字节) 来储存一个浮点数。 它可以表示十进制的15或16位有效数字,负值取值范围为 -1.7976E+308 到 -4.94065645841246544E-324,正值取值范围为 4.94065645841246544E-324 到 1.797693E+308

1.2.2、Double

Double是基于基本数据类型double的一个封装类,就是我们常用的java.lang.Double。

在这里插入图片描述

1.2.3、double和Double之间的关系

从jdk1.5开始,引入了“自动装箱”、“自动拆箱”的概念,简化了基本数据类型和包装类之间的转化,提高了使用效率。

关于什么是装箱、拆箱。举个栗子:比如我们常用的List就是一个自动装箱、拆箱的体现。

在这里插入图片描述

如图所示,楼主定义了一个Integer类型的list。当往list中放入数据1和2时,会把int类型自动转换为Integer类型,这个过程就是自动装箱。

在这里插入图片描述

反过来说,当从list中取出数据时,会把Integer类型自动转换为int类型,这个过程就是自动拆箱。

自动装箱最大的优点就是:可以直接使用包装类中所有的方法。

我们直接调用即可,方法内部都已经帮我们处理好了。感兴趣的可以看下源码深入了解哈,这里不再过多介绍。
在这里插入图片描述

二、两种类型数据的适用场景、优缺点

2.1、Double类型

适用场景:双精度、非重要性、或“相对模糊”的数据存储。比如:百分比计算。

缺点:数值容易丢失精度。

优点:执行效率高。

2.2、BigDecimal类型

适用场景:较为严谨的数值计算。比如:交易金额相关计算。

优点:数值计算较为精准。

缺点:较Double类型执行效率弱一些。

三、为什么不建议用Double类型计算金额

3.1、Double类型计算精度易丢失

当我们进行数值加减乘除运算时,Double类型很大程度上会产生误差。

如下图示例,楼主定义了2个Double类型数据的加减乘除,运算结果有3个产生了误差。有误差,但是误差不大。

在这里插入图片描述

那么为什么会存在这个问题呢?

可想而知,double类型在运算时,会先将数值转换成二进制然后再做运算。但是在转换过程中,可能会发生存储小数部分的位数不够的现象(无限循环小数),所以很大程度上可能会有非常小的误差产生

因此,不要直接使用入参double类型数值直接进行运算。可考虑使用string类型参数进行处理。

在这里插入图片描述

3.2、数值过大会变为科学记数法形式

当数值过大时,会变为这种科学记数法形式。

在这里插入图片描述
解决方案:可考虑使用BigDecimal类型进行转换。

四、BigDecimal常用方法

4.1、BigDecimal的初始化

楼主分别定义了2个不同类型的入参。

在这里插入图片描述

眼尖的小伙伴估计注意到了,实例化a对象时new BigDecimal(“0.12”)传入的是字符串,实例化b对象时new BigDecimal(0.12)传入的是double数值。大家觉得运行结果会一样吗?

如果你认为一样,那就错了。

在这里插入图片描述

字符串类型的输出了“0.12”,而double数值类型的却输出了“0.11999999999999999555910790149937383830547332763671875”。

在这里插入图片描述

这又回归到了上面说所的二进制转换存储小数部分的位数不够,造成的误差。使用double类型初始化BigDecimal对象,进行运算时可能会出现精度不准确的问题。

在这里插入图片描述

所以一定注意:不要使用double类型作为入参,直接去new一个BigDecimal对象!可能会有精度误差!

那么,为什么string就可以呢?

string是不可变的,定义为string之后再转换为数值肯定是固定的啊。

底层很多实现也是这个原理。比如BigDecimal.valueOf()方法,如果输入的是double类型,实质上源码中还是先转化为了字符串。

在这里插入图片描述

对于整型或保留小数位的,也有相应的处理方法。

在这里插入图片描述

方法有很多,大家想了解的可自行研究下,楼主就不再一一示例啦。

在这里插入图片描述

4.2、BigDecimal加法

BigDecimal add(BigDecimal augend)

在这里插入图片描述

两个BigDecimal类型数据相加,方法调用、及运行效果。

在这里插入图片描述

4.3、BigDecimal减法

BigDecimal subtract(BigDecimal subtrahend)

在这里插入图片描述
两个BigDecimal类型数据相减,方法调用、及运行效果。

在这里插入图片描述

4.4、BigDecimal乘法

BigDecimal multiply(BigDecimal multiplicand)

在这里插入图片描述

两个BigDecimal类型数据相乘,方法调用、及运行效果。

在这里插入图片描述

4.5、BigDecimal除法

BigDecimal divide(BigDecimal divisor)

在这里插入图片描述

两个BigDecimal类型数据相除,方法调用、及运行效果。

在这里插入图片描述

哎呦,惊喜不惊喜,意外不意外?相除的时候出现了无限循环小数

因此 相除的场景下尽可能设置保留小数位,可避免运算当中抛出异常

在这里插入图片描述

4.6、BigDecimal比较大小

楼主分别定义了2个值为“0.12”的数据a、b,以及值为“0.120”的数据c进行比较。

在这里插入图片描述

可以发现:相同值“0.12”通过双等号对比返回的结果是false,equals对比返回的是true。而“0.12”和“0.120”实质上数值大小是一样的,但通过双等号或equals进行对比,返回均为false

那么,对于BigDecimal类型如何比较大小呢?(叫我靓妹我就告诉你…)

在这里插入图片描述

方法就是:使用compareTo方法进行比较

在这里插入图片描述

a、b两值进行比较:a.compareTo(b) 。 结果为0表示a、b值相等。结果为-1表示a小于b。结果为1表示a大于b

4.7、BigDecimal工具类

为大家附上常用的操作工具类,直接调用即可。


package com.wss.demo.cas;

import java.math.BigDecimal;

public class ArithUtil {


    private static final int DEF_DIV_SCALE = 2; // 小数点后的保留位数

    /**
     * Double精确的加法运算
     *
     * @param d1 被加数
     * @param d2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(double d1, double d2) {
        BigDecimal value1 = new BigDecimal(Double.toString(d1));
        BigDecimal value2 = new BigDecimal(Double.toString(d2));
        return value1.add(value2);
    }

    /**
     * Double精确的减法运算
     *
     * @param d1 被减数
     * @param d2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(double d1, double d2) {
        BigDecimal value1 = new BigDecimal(Double.toString(d1));
        BigDecimal value2 = new BigDecimal(Double.toString(d2));
        return value1.subtract(value2);
    }

    /**
     * Double精确的乘法运算
     *
     * @param d1 被乘数
     * @param d2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(double d1, double d2) {
        BigDecimal value1 = new BigDecimal(Double.toString(d1));
        BigDecimal value2 = new BigDecimal(Double.toString(d2));
        return value1.multiply(value2);
    }

    /**
     * Double精确的除法运算, 当出现除不尽的情况时, 精确到小数点以后n位, 以后的数字四舍五入
     *
     * @param d1 被除数
     * @param d2 除数
     * @return 两个参数的商
     */
    public static BigDecimal div(double d1, double d2) {
        return div(d1, d2, DEF_DIV_SCALE);
    }

    /**
     * Double精确的除法运算, 当出现除不尽的情况时, 精确到小数点以后n位, 以后的数字四舍五入
     *
     * @param d1    被除数
     * @param d2    除数
     * @param scale 表示需要精确到小数点的后几位
     * @return 两个参数的商
     */
    public static BigDecimal div(double d1, double d2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("参数[scale]必须是正整数或者零");
        }
        BigDecimal value1 = new BigDecimal(Double.toString(d1));
        BigDecimal value2 = new BigDecimal(Double.toString(d2));
        return value1.divide(value2, scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 比较大小
     */
    public static boolean equalTo(BigDecimal b1, BigDecimal b2) {
        if(b1 == null || b2 == null) {
            return false;
        }
        return 0 == b1.compareTo(b2);
    }

}

嗯,今天的总结就先到这吧。散会了,别忘记给三婶儿点个赞哈~

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/115738.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!