科普文:浮点数精度运算BigDecimal踩坑和填坑

概叙

        用过Java的BigDecimal类型,但是很多人都用错了。如果使用不当,可能会造成非常致命的线上问题,因为这涉及到金额等数据的计算精度。

        首先说一下,一般对于不需要特别高精度的计算,我们使用double或float类型就可以了。

        由于计算机天生的无法表达完整的二进制浮点数的小数,二进制的小数是无限循环的,所以只能无限接近于精确值,这就造成了浮点计算的精度问题。此时就需要使用BigDecimal类型了。

 * 2. BigDecimal 类的使用* 2.1 创建 BigDecimal 对象* 要创建一个BigDecimal对象,我们可以使用以下几种方式:** 2.1.1 通过字符串创建* BigDecimal bd1 = new BigDecimal("123.45");* BigDecimal bd2 = new BigDecimal("67.89");* 2.1.2 通过整数值创建* BigDecimal bd3 = BigDecimal.valueOf(100);* BigDecimal bd4 = BigDecimal.valueOf(200);* 2.1.3 通过浮点数值创建* BigDecimal bd5 = BigDecimal.valueOf(3.14);* BigDecimal bd6 = BigDecimal.valueOf(2.71);* 2.2 常用方法* BigDecimal类提供了许多常用的方法,用于进行精确的数学运算。下面是一些常用的方法:** 2.2.1 加法* BigDecimal sum = bd1.add(bd2);* 2.2.2 减法* BigDecimal diff = bd1.subtract(bd2);* 2.2.3 乘法* BigDecimal product = bd1.multiply(bd2);* 2.2.4 除法* BigDecimal quotient = bd1.divide(bd2, RoundingMode.HALF_UP);* 2.2.5 取余* BigDecimal remainder = bd1.remainder(bd2);* 2.2.6 比较大小* int result = bd1.compareTo(bd2);* 2.2.7 取绝对值* BigDecimal absoluteValue = bd1.abs();* 2.2.8 取最大值* BigDecimal max = bd1.max(bd2);* 2.2.9 取最小值* BigDecimal min = bd1.min(bd2);* 2.3 精度设置* 在进行浮点数计算时,我们经常需要设置精度,以控制小数点后的位数。BigDecimal类提供了setScale方法,用于设置精度。* BigDecimal result = bd1.divide(bd2, 4, RoundingMode.HALF_UP);* 上面的代码将结果保留四位小数,并且使用了HALF_UP舍入模式。** 2.4 注意事项* 在使用BigDecimal进行浮点数计算时,需要注意以下几点:** 避免使用浮点数进行计算,而是使用字符串、整数或BigDecimal对象。* 使用适当的舍入模式来处理精度问题。* 不要使用equals方法进行BigDecimal对象的相等比较,而应该使用compareTo方法。

踩坑:浮点运算的精度坑

 1 - 0.8f  = 0.19999999

        float a=1;float b=0.8f;//a -b = 0.19999999// todo 打印结果会是0.2吗?不是,打印结果是0.19999999。//  因为b最大化接近于0.8,可能是0.80000001,近似于0.8。//  这就是为什么说精度要求不高时可以用double或float类型,//  一旦涉及到金额就不能使用浮点类型的原因。// a=1.0  b=0.8ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b));ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a -b = " + (a-b)));//2024-07-10 18:50:55 416	|	1720608655416	|	1	|	main	|	2024-07-10 18:45:55 413	|	1720608655416	|	1	|	main	|	a -b = 0.19999999

踩坑:空格等字符串转BigDecimal类型抛异常 NumberFormatException

        //BigDecimal a1 =new BigDecimal("00");//BigDecimal a2 =new BigDecimal(" 00 ");// todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal a22 =BigDecimalUtil.strToDecimalDefaultNull(" 00 "); // todo 工具转换//BigDecimal a2 =new BigDecimal("0 0");// todo  Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal a222 =BigDecimalUtil.strToDecimalDefaultNull("0 0");// todo 工具转换BigDecimal a2 =new BigDecimal("0.0");BigDecimal b1 = null ;//BigDecimal c1 =new BigDecimal("") ;// todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal c11 =BigDecimalUtil.strToDecimalDefaultNull("");// todo 工具转换BigDecimal c1 =new BigDecimal("0") ;//BigDecimal c2 =new BigDecimal(" ") ;// todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal c22 =BigDecimalUtil.strToDecimalDefaultNull(" ");// todo 工具转换BigDecimal c2 =new BigDecimal("0") ;//BigDecimal c3 =new BigDecimal("  ") ; // todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal c3 =new BigDecimal("01") ;// a22=null  a222=null //todo 工具类全部将其置为 nullZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a22=" + a22 +"  a222=" + a222));// c11=null  c22=null  //todo 工具类全部将其置为 nullZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("c11=" + c11 +"  c22=" + c22));//a1=0  a2=0.0ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a1=" + a1 +"  a2=" + a2));//b1=null   c1=0ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("b1=" + b1+"   c1=" + c1));//c2=0  c3=1ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("c2=" + c2 +"  c3=" + c3));

踩坑:使用BigDecimal构造函数和使用valueOf方法初始化,两种方式得到的结果不一样

     public static void  test1() {BigDecimal a = BigDecimal.ONE;// todo 踩坑: 使用BigDecimal构造函数和使用valueOf方法初始化,两种方式得到的结果不一样,BigDecimal b = new BigDecimal(0.8);// valueOf方法初始化的BigDecimal数据计算是精确的。BigDecimal c = BigDecimal.valueOf(0.8);// todo 使用BigDecimal构造函数时,传字符串而不要传浮点类型。尽量使用valueOf方法初始化,并且不要传float类型数据。// a=1  b=0.8000000000000000444089209850062616169452667236328125  c=0.8ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b+"  c=" + c));// a-b=0.1999999999999999555910790149937383830547332763671875ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a-b=" + (a.subtract(b))));// a-c=0.2ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a-c=" + (a.subtract(c))));}

踩坑:equals判断a和b不相等(含精度比较),compareTo判断a和b相等(大小比较,不含精度)

    public static void  test2(){BigDecimal a=new BigDecimal("0.02");BigDecimal b=new BigDecimal( "0.020");// a=0.02  b=0.020  todo 精度不一样ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b));// todo 踩坑: equals判断a和b不相等,compareTo判断a和b相等// a.equals(b):false  // todo equals除了比较值的大小,还会比较值的精度。 todo 比较大小且限制精度,使用equals方法。ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a.equals(b):"+ a.equals(b)));// a.compareTo(b):0 // todo 比较两个BigDecimal值的大小,使用compareTo方法。ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a.compareTo(b):"+(a.compareTo(b))));}

等值比较

踩坑:使用BigDecimal进行计算时,结果一定要设置精度和舍入模式。

    public static void  test3(){BigDecimal a=new BigDecimal("1.0");BigDecimal b=new BigDecimal("3.0");// todo 在进行BigDecimal计算特别是除法计算时,很多人会忘记设置精度和舍入模式,最终结果就是程序会报一个ArithmeticException异常。//a.divide(b); //意思就是如果divide方法计算得到的商是一个无限小数,而代码预期得到一个精确数字,那么就会抛出ArithmeticException异常。/***  todo  踩坑:使用BigDecimal进行计算时,结果一定要设置精度和舍入模式。* 如果商具有非终止的十进制展开,并且指定运算返回精确的结果,则引发ArithmeticException。* 否则,将返回除法的确切结果,就像对其他操作所做的那样。* Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.* 	at java.math.BigDecimal.divide(BigDecimal.java:1690)* 	at com.zxx.study.base.scale.BigDecimalError.test3(BigDecimalError.java:47)* 	at com.zxx.study.base.scale.BigDecimalError.main(BigDecimalError.java:23)* */// a=1.0  b=3.0ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b));// 设置了c的精度为2,RoundingMode.HALF_UP是向上舍入模式,即四舍五入。BigDecimal c=a.divide(b,2, RoundingMode.HALF_UP);// a/b=0.33ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a/b="+ c));}

 踩坑:toString方法将BigDecimal的值转成了科学计数法的值

    public static void  test4(){BigDecimal   a= BigDecimal.valueOf(567584568686785678678.6786786785978987090456);// 567584568686785678678.6786786785978987090456 =5.6758456868678566E+20// todo 踩坑: toString方法将BigDecimal的值转成了科学计数法的值。ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread(" 567584568686785678678.6786786785978987090456 ="+ a));/*** 我们可以先看下BigDecimal三种转字符串的方法。* toString():如果需要指数,则使用科学计数法。* toPlainString():不带指数的字符串表现形式。* toEngineeringString():如果需要指数,则使用工程计数法。* todo 利用NumberFormat类,对BigDecimal进行格式化控制。* */}

利用NumberFormat类,对BigDecimal进行格式化控制。

    public static void  test5(){// todo 利用NumberFormat类,对BigDecimal进行格式化控制。// NumberFormat.getCurrencyInstance()方法用于获取一个货币格式化的实例,该实例可以根据默认或指定的地区设置将数字格式化为货币值。NumberFormat currency=NumberFormat.getCurrencyInstance();NumberFormat percent = NumberFormat.getPercentInstance();percent.setMaximumFractionDigits(3);//百分比小数点最多3位BigDecimal loanAmount =new BigDecimal("60000.88");//金额BigDecimal interestRate=new BigDecimal( "0.008");//利率BigDecimal interest=loanAmount.multiply(interestRate);//相乘// loanAmount=60000.88  interestRate=0.008  interest=480.00704ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("loanAmount=" + loanAmount +"  interestRate=" + interestRate+"  interest=" + interest));// 创建一个法国的货币格式化实例//        NumberFormat n = NumberFormat.getCurrencyInstance(Locale.FRANCE);//金硕:	¥60,000.88  todo 当前环境变量是中国  所以用人民币格式展示ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("金硕:\t"+ currency.format(loanAmount)));// 利率:	0.8%ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("利率:\t"+ percent.format(interestRate)));// 利息:	¥480.01  todo 当前环境变量是中国  所以用人民币格式展示  todo这里还默认四舍五入了ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("利息:\t"+currency.format(interest)));}

BigDecimalUtil工具类

1.使用工具类中String方法进行运算

2.运算时,指定精度和舍入模式

3.默认值和默认精度设置

    /*** 字符串转BigDecimal 为空则返回Zero*/public static BigDecimal strToDecimalDefaultZero(String s) {// todo 数据为空则返回Zeroreturn strToDecimal(s, BigDecimal.ZERO);}/*** 字符串转BigDecimal 为空则返回空*/public static BigDecimal strToDecimalDefaultNull(String s) {return strToDecimal(s, null);}/*** 字符串转BigDecimal 为空默认值defaultV*/private static BigDecimal strToDecimal(String s, BigDecimal defaultV) {if (ObjectUtils.isEmpty(s)) {// todo 默认值为 nullreturn defaultV;}try {// todo 默认精度4位小数  且四舍五入return new BigDecimal(s).setScale(scaleDefault, roundingModeDefault);} catch (Exception e) {/*** todo new BigDecimal(s).setScale 会抛出两种异常**舍入模式 =7 ROUND_UNNECESSARY 会报算数异常 ArithmeticException – if roundingMode==ROUND_UNNECESSARY and the specified scaling operation would require rounding.*s舍入模式 不在[0,7] 会报无效参数异常 IllegalArgumentException – if roundingMode does not represent a valid rounding mode.* */// todo 精度设置异常  则返回nullreturn defaultV;}}

完整代码

踩坑代码

package com.zxx.study.base.scale;import com.zxx.study.base.util.ZhouxxTool;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;/*** @author zhouxx* @create 2024-07-10 18:43*/
public class BigDecimalError {public static void main(String[] args) {float a=1;float b=0.8f;//a -b = 0.19999999// todo 打印结果会是0.2吗?不是,打印结果是0.19999999。//  因为b最大化接近于0.8,可能是0.80000001,近似于0.8。//  这就是为什么说精度要求不高时可以用double或float类型,//  一旦涉及到金额就不能使用浮点类型的原因。// a=1.0  b=0.8ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b));ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a -b = " + (a-b)));//2024-07-10 18:50:55 416	|	1720608655416	|	1	|	main	|	2024-07-10 18:45:55 413	|	1720608655416	|	1	|	main	|	a -b = 0.19999999//BigDecimal a1 =new BigDecimal("00");//BigDecimal a2 =new BigDecimal(" 00 ");// todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal a22 =BigDecimalUtil.strToDecimalDefaultNull(" 00 "); // todo 工具转换//BigDecimal a2 =new BigDecimal("0 0");// todo  Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal a222 =BigDecimalUtil.strToDecimalDefaultNull("0 0");// todo 工具转换BigDecimal a2 =new BigDecimal("0.0");BigDecimal b1 = null ;//BigDecimal c1 =new BigDecimal("") ;// todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal c11 =BigDecimalUtil.strToDecimalDefaultNull("");// todo 工具转换BigDecimal c1 =new BigDecimal("0") ;//BigDecimal c2 =new BigDecimal(" ") ;// todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal c22 =BigDecimalUtil.strToDecimalDefaultNull(" ");// todo 工具转换BigDecimal c2 =new BigDecimal("0") ;//BigDecimal c3 =new BigDecimal("  ") ; // todo Exception in thread "main" java.lang.NumberFormatExceptionBigDecimal c3 =new BigDecimal("01") ;// a22=null  a222=null //todo 工具类全部将其置为 nullZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a22=" + a22 +"  a222=" + a222));// c11=null  c22=null  //todo 工具类全部将其置为 nullZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("c11=" + c11 +"  c22=" + c22));//a1=0  a2=0.0ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a1=" + a1 +"  a2=" + a2));//b1=null   c1=0ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("b1=" + b1+"   c1=" + c1));//c2=0  c3=1ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("c2=" + c2 +"  c3=" + c3));test1();test2();test3();test4();test5();}public static void  test1() {BigDecimal a = BigDecimal.ONE;// todo 踩坑: 使用BigDecimal构造函数和使用valueOf方法初始化,两种方式得到的结果不一样,BigDecimal b = new BigDecimal(0.8);// valueOf方法初始化的BigDecimal数据计算是精确的。BigDecimal c = BigDecimal.valueOf(0.8);// todo 使用BigDecimal构造函数时,传字符串而不要传浮点类型。尽量使用valueOf方法初始化,并且不要传float类型数据。// a=1  b=0.8000000000000000444089209850062616169452667236328125  c=0.8ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b+"  c=" + c));// a-b=0.1999999999999999555910790149937383830547332763671875ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a-b=" + (a.subtract(b))));// a-c=0.2ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a-c=" + (a.subtract(c))));}public static void  test2(){BigDecimal a=new BigDecimal("0.02");BigDecimal b=new BigDecimal( "0.020");// a=0.02  b=0.020  todo 精度不一样ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b));// todo 踩坑: equals判断a和b不相等,compareTo判断a和b相等// a.equals(b):false  // todo equals除了比较值的大小,还会比较值的精度。 todo 比较大小且限制精度,使用equals方法。ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a.equals(b):"+ a.equals(b)));// a.compareTo(b):0 // todo 比较两个BigDecimal值的大小,使用compareTo方法。ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a.compareTo(b):"+(a.compareTo(b))));}public static void  test3(){BigDecimal a=new BigDecimal("1.0");BigDecimal b=new BigDecimal("3.0");// todo 在进行BigDecimal计算特别是除法计算时,很多人会忘记设置精度和舍入模式,最终结果就是程序会报一个ArithmeticException异常。//a.divide(b); //意思就是如果divide方法计算得到的商是一个无限小数,而代码预期得到一个精确数字,那么就会抛出ArithmeticException异常。/***  todo  踩坑:使用BigDecimal进行计算时,结果一定要设置精度和舍入模式。* 如果商具有非终止的十进制展开,并且指定运算返回精确的结果,则引发ArithmeticException。* 否则,将返回除法的确切结果,就像对其他操作所做的那样。* Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.* 	at java.math.BigDecimal.divide(BigDecimal.java:1690)* 	at com.zxx.study.base.scale.BigDecimalError.test3(BigDecimalError.java:47)* 	at com.zxx.study.base.scale.BigDecimalError.main(BigDecimalError.java:23)* */// a=1.0  b=3.0ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a=" + a +"  b=" + b));// 设置了c的精度为2,RoundingMode.HALF_UP是向上舍入模式,即四舍五入。BigDecimal c=a.divide(b,2, RoundingMode.HALF_UP);// a/b=0.33ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("a/b="+ c));}public static void  test4(){BigDecimal   a= BigDecimal.valueOf(567584568686785678678.6786786785978987090456);// 567584568686785678678.6786786785978987090456 =5.6758456868678566E+20// todo 踩坑: toString方法将BigDecimal的值转成了科学计数法的值。ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread(" 567584568686785678678.6786786785978987090456 ="+ a));/*** 我们可以先看下BigDecimal三种转字符串的方法。* toString():如果需要指数,则使用科学计数法。* toPlainString():不带指数的字符串表现形式。* toEngineeringString():如果需要指数,则使用工程计数法。* todo 利用NumberFormat类,对BigDecimal进行格式化控制。* */}public static void  test5(){// todo 利用NumberFormat类,对BigDecimal进行格式化控制。// NumberFormat.getCurrencyInstance()方法用于获取一个货币格式化的实例,该实例可以根据默认或指定的地区设置将数字格式化为货币值。NumberFormat currency=NumberFormat.getCurrencyInstance();NumberFormat percent = NumberFormat.getPercentInstance();percent.setMaximumFractionDigits(3);//百分比小数点最多3位BigDecimal loanAmount =new BigDecimal("60000.88");//金额BigDecimal interestRate=new BigDecimal( "0.008");//利率BigDecimal interest=loanAmount.multiply(interestRate);//相乘// loanAmount=60000.88  interestRate=0.008  interest=480.00704ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("loanAmount=" + loanAmount +"  interestRate=" + interestRate+"  interest=" + interest));// 创建一个法国的货币格式化实例//        NumberFormat n = NumberFormat.getCurrencyInstance(Locale.FRANCE);//金硕:	¥60,000.88  todo 当前环境变量是中国  所以用人民币格式展示ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("金硕:\t"+ currency.format(loanAmount)));// 利率:	0.8%ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("利率:\t"+ percent.format(interestRate)));// 利息:	¥480.01  todo 当前环境变量是中国  所以用人民币格式展示  todo这里还默认四舍五入了ZhouxxTool.printTimeAndThread(ZhouxxTool.getTimeAndThread("利息:\t"+currency.format(interest)));}
}

BigDecimalUtil工具类

package com.zxx.study.base.scale;import org.apache.commons.lang3.ObjectUtils;import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Objects;/*** 1. 简介* 在Java中,我们经常需要进行精确的浮点数运算。然而,由于计算机的二进制表示方式和十进制表示方式之间的差异,导致了在浮点数计算中可能存在精度丢失的问题。为了解决这个问题,Java提供了BigDecimal类,它可以处理任意精度的浮点数运算。** BigDecimal类是Java标准库中的一部分,它提供了大量的方法和功能,用于进行高精度的数学运算。在本文中,我们将介绍如何使用BigDecimal类进行精确的计算,并提供了一些常用的工具方法。** BigDecimal类位于java.math包中,它提供了一系列方法来进行超过16位有效位的精确运算。以下是一些常用的BigDecimal方法:** 加法: add(BigDecimal) - 返回两个BigDecimal对象中的值相加的结果。* 减法: subtract(BigDecimal) - 返回两个BigDecimal对象中的值相减的结果。* 乘法: multiply(BigDecimal) - 返回两个BigDecimal对象中的值相乘的结果。* 除法: divide(BigDecimal) - 返回两个BigDecimal对象中的值相除的结果。* 整除: divideAndRemainder(BigDecimal) - 返回两个BigDecimal对象的值相除后的商和余数。例如,n.divideAndRemainder(m)可以判断n是否是m的整数倍数。* 比较大小: compareTo(BigDecimal) - 比较此BigDecimal与指定的BigDecimal的大小。* 取绝对值: abs() - 返回此BigDecimal的绝对值。* 四舍五入: setScale(int newScale, RoundingMode roundingMode) - 设置此BigDecimal的小数位数并四舍五入。* 截断: stripTrailingZeros() - 去掉此BigDecimal尾部多余的零。** 2. BigDecimal 类的使用* 2.1 创建 BigDecimal 对象* 要创建一个BigDecimal对象,我们可以使用以下几种方式:** 2.1.1 通过字符串创建* BigDecimal bd1 = new BigDecimal("123.45");* BigDecimal bd2 = new BigDecimal("67.89");* 2.1.2 通过整数值创建* BigDecimal bd3 = BigDecimal.valueOf(100);* BigDecimal bd4 = BigDecimal.valueOf(200);* 2.1.3 通过浮点数值创建* BigDecimal bd5 = BigDecimal.valueOf(3.14);* BigDecimal bd6 = BigDecimal.valueOf(2.71);* 2.2 常用方法* BigDecimal类提供了许多常用的方法,用于进行精确的数学运算。下面是一些常用的方法:** 2.2.1 加法* BigDecimal sum = bd1.add(bd2);* 2.2.2 减法* BigDecimal diff = bd1.subtract(bd2);* 2.2.3 乘法* BigDecimal product = bd1.multiply(bd2);* 2.2.4 除法* BigDecimal quotient = bd1.divide(bd2, RoundingMode.HALF_UP);* 2.2.5 取余* BigDecimal remainder = bd1.remainder(bd2);* 2.2.6 比较大小* int result = bd1.compareTo(bd2);* 2.2.7 取绝对值* BigDecimal absoluteValue = bd1.abs();* 2.2.8 取最大值* BigDecimal max = bd1.max(bd2);* 2.2.9 取最小值* BigDecimal min = bd1.min(bd2);* 2.3 精度设置* 在进行浮点数计算时,我们经常需要设置精度,以控制小数点后的位数。BigDecimal类提供了setScale方法,用于设置精度。* BigDecimal result = bd1.divide(bd2, 4, RoundingMode.HALF_UP);* 上面的代码将结果保留四位小数,并且使用了HALF_UP舍入模式。** 2.4 注意事项* 在使用BigDecimal进行浮点数计算时,需要注意以下几点:** 避免使用浮点数进行计算,而是使用字符串、整数或BigDecimal对象。* 使用适当的舍入模式来处理精度问题。* 不要使用equals方法进行BigDecimal对象的相等比较,而应该使用compareTo方法。** @author zhouxx* @create 2024-07-10 20:21*/
public class BigDecimalUtil {public static final BigDecimal per = new BigDecimal("100");public static final BigDecimal thousand = new BigDecimal("1000");public static final int scaleDefault = 4; // 默认精度 小数点后4位public static final RoundingMode roundingModeDefault = RoundingMode.HALF_UP ; // 默认舍入模式 四舍五入/*** 多个BigDecimal数据相乘*/public static BigDecimal multiply(BigDecimal from, BigDecimal... to) {BigDecimal result = safe(from);if (to != null) {for (BigDecimal t : to) {result = result.multiply(safe(t));}}return result;}/*** 多个字符串数据相乘*/public static BigDecimal multiply(String from, String... to) {BigDecimal result = strToDecimalDefaultZero(from);if (to != null) {for (String t : to) {result = result.multiply(safe(strToDecimalDefaultZero(t)));}}return result;}/*** 多个BigDecimal数据相加*/public static BigDecimal add(BigDecimal from, BigDecimal... to) {BigDecimal result = safe(from);if (to != null) {for (BigDecimal t : to) {result = result.add(safe(t));}}return result;}/*** 多个字符串相加*/public static String add(String from, String... to) {BigDecimal result = strToDecimalDefaultZero(from);if (to != null) {for (String t : to) {result = add(result, t);}}return toString(result);}/*** 第一个为BigDecimal 第二个为字符串*/public static BigDecimal add(BigDecimal from, String to) {BigDecimal result = from;if (to != null && !to.isEmpty()) {result = result.add(strToDecimalDefaultZero(to));}return result;}/*** 减法 多个减数*/public static BigDecimal subtract(BigDecimal from, BigDecimal... to) {BigDecimal result = safe(from);if (to != null) {for (BigDecimal t : to) {result = result.subtract(safe(t));}}return result;}/*** 除法 保留两位小数 除数为0 则为0*/public static BigDecimal divide2(BigDecimal from, BigDecimal to) {BigDecimal result = safe(from);if (to == null) {return result;} else if (to.compareTo(BigDecimal.ZERO) == 0) {return BigDecimal.ZERO;} else {return result.divide(to, 2, roundingModeDefault);}}/*** BigDecimal除法 然后设置scale 除数为0,为空都结果为null*/public static BigDecimal divide3(BigDecimal from, BigDecimal to) {BigDecimal result = safe(from);if (to == null || to.compareTo(BigDecimal.ZERO) == 0) {return null;} else {return result.divide(to, 3, roundingModeDefault);}}/*** BigDecimal除法 然后设置scale 除数为0,为空都结果为0*/public static BigDecimal divide(BigDecimal from, BigDecimal to, Integer scale, RoundingMode roundingMode) {BigDecimal result = safe(from);if (to == null) {return result;} else if (to.compareTo(BigDecimal.ZERO) == 0) {return BigDecimal.ZERO;} else {scale = scale < 0 ? scaleDefault : scale;return result.divide(to, scale, roundingMode);}}/*** 字符串除法 然后设置scale*/public static BigDecimal divide(String from, String to, Integer scale, RoundingMode roundingMode) {BigDecimal decimalFrom = strToDecimalDefaultNull(from);BigDecimal decimalTo = strToDecimalDefaultNull(to);return divide(decimalFrom, decimalTo, scale, roundingMode);}/*** 计算百分比*/public static BigDecimal dividePer(BigDecimal from, BigDecimal to) {// null运算 todo 返回nullif (Objects.isNull(from)|| Objects.isNull(to)) {return null;} else {from = multiply(from, per);return divide(from, to, scaleDefault,roundingModeDefault);}}/*** 千分级 小数转千分比*/public static BigDecimal multiplyThousand(BigDecimal baseVal) {BigDecimal safe = safe(baseVal);return multiply(safe, thousand);}/*** 千分级 转成千分小数*/public static BigDecimal divideThousand(BigDecimal baseVal) {BigDecimal safe = safe(baseVal);return divide(safe, thousand, scaleDefault,roundingModeDefault);}/*** 百分比转成小数*/public static BigDecimal divideHundred(BigDecimal baseVal) {BigDecimal safe = safe(baseVal);return divide(safe, per, scaleDefault,roundingModeDefault);}/*** 小数转成百分比的值*/public static BigDecimal multiplyHundred(BigDecimal baseVal) {BigDecimal safe = safe(baseVal);return multiply(safe, per);}/*** 比较两个BigDecimal数据大小 返回int*/public static int compareTo(BigDecimal from, BigDecimal to) {return safe(from).compareTo(safe(to));}/*** 比较两个BigDecimal数据大小 返回true false*/public static boolean greaterThan(BigDecimal from, BigDecimal to) {return safe(from).compareTo(safe(to)) > 0;}/*** 比较两个字符串数据比较*/public static int compareTo(String from, String to) {BigDecimal decimalFrom = strToDecimalDefaultZero(from);BigDecimal decimalTo = strToDecimalDefaultZero(to);return safe(decimalFrom).compareTo(safe(decimalTo));}/*** 数据为空则返回Zero*/private static BigDecimal safe(BigDecimal target) {// todo 数据为空则返回Zeroif (Objects.isNull(target)) {return BigDecimal.ZERO;} else {return target;}}/*** 字符串转BigDecimal 为空则返回Zero*/public static BigDecimal strToDecimalDefaultZero(String s) {// todo 数据为空则返回Zeroreturn strToDecimal(s, BigDecimal.ZERO);}/*** 字符串转BigDecimal 为空则返回空*/public static BigDecimal strToDecimalDefaultNull(String s) {return strToDecimal(s, null);}/*** 字符串转BigDecimal 为空默认值defaultV*/private static BigDecimal strToDecimal(String s, BigDecimal defaultV) {if (ObjectUtils.isEmpty(s)) {// todo 默认值为 nullreturn defaultV;}try {// todo 默认精度4位小数  且四舍五入return new BigDecimal(s).setScale(scaleDefault, roundingModeDefault);} catch (Exception e) {/*** todo new BigDecimal(s).setScale 会抛出两种异常**舍入模式 =7 ROUND_UNNECESSARY 会报算数异常 ArithmeticException – if roundingMode==ROUND_UNNECESSARY and the specified scaling operation would require rounding.*s舍入模式 不在[0,7] 会报无效参数异常 IllegalArgumentException – if roundingMode does not represent a valid rounding mode.* */// todo 精度设置异常  则返回nullreturn defaultV;}}/*** BigDecimal值如果为空转null*/public static BigDecimal decimalDefaultNull(BigDecimal val) {if (ObjectUtils.isEmpty(val)) {return null;}try {// // todo 默认精度4位小数  且四舍五入return val.setScale(scaleDefault, roundingModeDefault);} catch (Exception e) {return null;}}/*** BigDecimal值如果为空转BigDecimal-ZERO*/public static BigDecimal decimalDefaultZero(BigDecimal val) {return ObjectUtils.isEmpty(val) ? BigDecimal.ZERO : val;}/*** 转换为字符串 6位小数*/public static String toString(BigDecimal decimal) {DecimalFormat decimalFormat = new DecimalFormat("#.######");decimalFormat.setGroupingUsed(false);return decimal == null ? null : decimalFormat.format(decimal);}/*** 根据pattern格式化decimal数据*/public static String toString(BigDecimal decimal, String pattern) {DecimalFormat decimalFormat = new DecimalFormat(pattern);decimalFormat.setGroupingUsed(false);return decimal == null ? null : decimalFormat.format(decimal);}/*** 设置小数位数,四舍五入*/public static BigDecimal setScale(BigDecimal bigDecimal, int scale) {if (bigDecimal == null) {return null;}scale = scale< 0 ? scaleDefault : scale;return bigDecimal.divide(new BigDecimal(1), scale, roundingModeDefault);}/*** 减法 减去多个值*/public static BigDecimal subtract(String from, String... to) {BigDecimal result = strToDecimalDefaultZero(from);if (to != null) {for (String t : to) {result = result.subtract(strToDecimalDefaultZero(t));}}return result;}/*** 字符串数value 据转成BigDecimal 默认值defaultV*/private static BigDecimal strToDecimalToDefaultV(String value, BigDecimal defaultV, int scale) {if (ObjectUtils.isEmpty(value)) {return defaultV;}try {return new BigDecimal(value).setScale(scale, RoundingMode.HALF_UP);} catch (Exception e) {return defaultV;}}/*** 设置对象里面的所有的BigDecimal,保留小数位数scale位*/public static void handleBigDecimalScale(Object o, int scale) {Field[] fields = o.getClass().getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);if (field.getType() == BigDecimal.class) {Object v = field.get(o);if (v instanceof BigDecimal) {BigDecimal b = (BigDecimal) v;b = setScale(b, scale);field.set(o, b);}}} catch (Exception ignored) {}}}/*** double-BigDecimal scale位小数*/public static BigDecimal doubleValueSetScale(double value, int scale) {return setScale(new BigDecimal(value), scale);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/44778.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习理论基础】回归模型定义和分类

定义 回归分析是研究自变量与因变量之间数量变化关系的一种分析方法&#xff0c;它主要是通过因变量 Y Y Y与影响它的自变量 X i X_i Xi​ 之间的回归模型&#xff0c;衡量自变量 X i X_i Xi​ 对因变量 Y Y Y 的影响能力的&#xff0c;进而可以用来预测因变量Y的发展趋势。…

中国AI已遥遥领先

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 种种迹象表明&#xff0c;中国的AI产业是仅次于美国的存在&#xff0c;中国的AI已经遥遥领先&#xff0c;其他国家。 根据中国信息通信研究院发布的报告称&#xff1a; 根据中国信息通信研究院近日发布的《全球…

【C++深度探索】全面解析多态性机制(一)

hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1…

万物皆可嵌入--embedding在GPU中的实现

摘要 Embedding技术自从谷歌推出word2vec的工作后得到迅速的应用&#xff0c;典型应用之一是在广告推荐场景中&#xff0c;从word2vec演进到item2vec&#xff0c;embedding技术的出现也使深度学习进入广告推荐的场景成为可能。广告推荐模型动辄几十GB甚至TB的模型大小&#xf…

数据结构(Java):集合类LinkedList集合类Stack

1、集合类LinkedList 1.1 什么是LinkedList LinkedList的底层是一个双向链表的结构&#xff08;故不支持随机访问&#xff09;&#xff1a; 在LinkedList中&#xff0c;定义了first和last&#xff0c;分别指向链表的首节点和尾结点。 每个节点中有一个成员用来存储数据&…

TikTok短视频矩阵管理系统源码

在数字化浪潮汹涌的今天&#xff0c;短视频已成为人们生活中不可或缺的一部分。TikTok作为短视频领域的佼佼者&#xff0c;其用户基数庞大&#xff0c;影响力深远。然而&#xff0c;对于众多内容创作者和营销人员来说&#xff0c;如何高效管理多个TikTok账号&#xff0c;实现批…

分布式训练

一、分布式计算 跟多GPU不同是&#xff1a;数据不是从主存拿的&#xff0c;是在分布式文件系统拿的&#xff0c;有多个工作站&#xff0c;工作站中有多个GPU&#xff0c;通过网络读取数据到GPU中&#xff0c;GPU通过网络接收到来自参数服务器的参数进行运算计算梯度&#xff0c…

Biotinylated L-Thyroxine (T4) ;生物素 L-甲状腺素(T4)

一、基本信息 常用名&#xff1a;Biotinylated L-Thyroxine (T4) 生物素 L-甲状腺素(T4) 英文名称&#xff1a;Biotinylated L-Thyroxine (T4) 中文名称&#xff1a;生物素 L-甲状腺素(T4) 二、组成与性质 生物素&#xff1a;一种水溶性维生素&#xff0c;也称为维生素B7&#…

Photoshop批量处理图片分辨率

整理一些文件的时候&#xff0c;发现需要处理大量图片的尺寸和分辨率。如果一张一张的处理就会很慢&#xff0c;搜了下&#xff0c;Photoshop提供自动批量处理的方法。在此记录一下。 一、说说批量处理图片 1.打开PS软件并导入图片&#xff0c;我用的是比较老的版本cs4&#…

在创建jsp项目中解决无法连接数据库以及junit问题

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

将 Vision Transformer 用于医学图像的语义分割

关于ViT的关键点如下&#xff1a; ViT架构基于将图像表示为一组补丁。图像补丁是图像的非重叠块。每个块最初都有一个由该块中的图像像素形成的嵌入向量。Transformer编码器是ViT的主要部分&#xff0c;它根据它们的类别归属来训练补丁之间的相似度。它包含一系列线性、归一化…

拥抱UniHttp,规范Http接口对接之旅

前言 如果你项目里还在用传统的编程式Http客户端比如HttpClient、Okhttp去直接对接第三方Http接口&#xff0c; 那么你项目一定充斥着大量的对接逻辑和代码&#xff0c; 并且针对不同的对接渠道方需要每次封装一次调用的简化&#xff0c; 一旦封装不好系统将会变得难以维护&am…

SprintBoot创建遇到的问题

最近使用IDEA版本为2022.3.1&#xff0c;java版本为21.0.3&#xff0c;现在做一个创建SprintBoot3的一个大体流程 1.先下载Maven&#xff0c;解压到一个位置 maven下载 2.配置setting.xml文件 这路径自己配置&#xff0c;这里不多演示 代码如下&#xff1a; <mirror>&…

前端如何取消接口调用

&#x1f9d1;‍&#x1f4bb; 写在开头 点赞 收藏 学会&#x1f923;&#x1f923;&#x1f923; 1. xmlHttpRequest是如何取消请求的&#xff1f; 实例化的XMLHttpRequest对象上也有abort方法 const xhr new XMLHttpRequest(); xhr.addEventListener(load, function(e)…

开关电源——15种控制模式(1)

关于开关电源的控制模式&#xff0c;TI官网的控制模式快速参考指南有相对全面的归纳和描述&#xff0c;提供了15种不同的控制架构&#xff0c;这些架构涵盖了从基础到高级的多种控制模式&#xff0c;以适应不同的应用需求&#xff0c;如下表所示&#xff1a; 以下是对控制模式相…

记一次Ueditor上传Bypss

前言 前一段时间和小伙伴在某内网进行渗透测试&#xff0c;目标不给加白&#xff0c;只能进行硬刚了&#xff0c;队友fscan一把梭发现某资产疑似存在Ueditor组件&#xff0c;但初步测试是存在waf和杀软的&#xff0c;无法进行getshell&#xff0c;经过一番折腾最终getshell&am…

一个 Docker 搭建的自动化电视剧下载和管理工具

大家好,我是CodeQi! 一个标准的“追剧狂人”。每周都有新剧上线,每天都要时刻关注各大影视平台的更新,这无疑是一项体力与脑力并存的艰巨任务。 于是,我决定为自己打造一个自动化的电视剧下载和管理工具。作为一个程序员,用 Docker 搭建这种自动化工具简直是小菜一碟。…

PointCloudLib GridMinimum获取栅格最低点 C++版本

测试效果 简介 在点云库(Point Cloud Library, PCL)中,如果你想要获取一个栅格(Grid)内的最低点,这通常意味着你需要先对点云数据进行某种形式的栅格化处理,然后在每个栅格内寻找最低的点。 测试代码 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointC…

f_mkfs格式化最小分区数是191

使用fatfs的f_mkfs最小分区数是191原因&#xff1a; 在挂载ram_disk时参考的文章有提到&#xff1a; “然后是GET_SECTOR_COUNT 用于f_mkfs格式化时获取可用的sector的数量&#xff0c;32bit-LBA的情况下至少为191” 自己也实际试过确实要不少于191&#xff0c;网上也没找到相…

【单片机毕业设计选题24056】-基于STM32的八路抢答器设计

系统功能: 系统上电后显示“欢迎使用八路抢答系统请稍后”&#xff0c;两秒后进入正常页面显示。 第一行显示系统状态信息&#xff0c;第二行显示抢答计时时间&#xff0c;第三行显示设定的抢答时间&#xff0c; 第四行显示系统状态&#xff08;空闲状态或计时状态&#xff…