当前位置: 首页 > 科技观察

你真的知道如何在Java中使用BigDecimal吗?

时间:2023-03-13 14:52:09 科技观察

1。BigDecimal概述Java在java.math包中提供的API类BigDecimal用于对有效位数大于16位的数字进行精确运算。双精度浮点变量double可以处理16位有效数字,但在实际应用中,可能需要对更大或更小的数进行运算处理。一般来说,对于不需要精确计算精度的数字,我们可以直接使用Float和Double,但是Double.valueOf(String)和Float.valueOf(String)会损失精度。所以在开发中,如果我们需要精确的计算结果,就必须使用BigDecimal类来操作。BigDecimal创建的是一个对象,所以我们不能使用+、-、*、/等传统算术运算符直接对其对象进行数学运算,必须调用其对应的方法。方法中的参数也必须是BigDecimal对象。构造函数是类的一种特殊方法,旨在创建对象,尤其是带参数的对象。2.BigDecimal常用构造函数2.1.常用构造函数BigDecimal(int)用参数指定的整数值创建对象BigDecimal(double)用参数指定的双精度值创建对象BigDecimal(long)用指定参数创建对象longintegervalueBigDecimal(String)创建一个对象,其值由参数指定的值表示为字符串);System.out.println("=====================");BigDecimalb=newBigDecimal("0.1");System.out.println("bvaluesis:"+b);结果示例:avaluesis:0.100000000000000055511151231257827021181583404541015625===========================================================================================================================================================有一定的不可预测性。有人可能认为在Java中编写newBigDecimal(0.1)会创建一个正好等于0.1的BigDecimal(未缩放的值1,其缩放比例为1),但它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能精确地表示为双精度数(或者就此而言,表示为任何有限长度的二进制小数)。因此,传递给构造函数的值不会完全等于0.1(尽管显然等于该值)。2)String构造函数是完全可预测的:编写newBigDecimal("0.1")将创建一个BigDecimal,它正好等于预期的0.1。所以相比较而言,一般还是推荐优先使用String构造方法。3)当必须使用double作为BigDecimal的来源时,请注意此构造函数提供了精确转换;它不提供与首先使用Double.toString(double)方法,然后使用BigDecimal(String)构造函数,将double转换为String相同的结果。要获得该结果,请使用静态valueOf(double)方法。Java友公众号回复“面试题集锦”,送你面试题集锦。3、BigDecimal常用方法详解3.1.常用方法add(BigDecimal)将BigDecimal对象中的值相加并返回BigDecimal对象对象,返回BigDecimal对象divide(BigDecimal)将BigDecimal对象中的值相除,返回BigDecimal对象toString()将BigDecimal对象中的值转换为字符串doubleValue()将BigDecimal对象中的值转换为double-precisionnumberfloatValue()将BigDecimal对象中的值转换为单精度数longValue()将BigDecimal对象中的值转换为长整数intValue()将BigDecimal对象中的值转换3.2、BigDecimal大小比较在java中,BigDecimal的compareTo方法一般用于比较BigDecimal的大小inta=bigdemical.compareTo(bigdemical2)返回结果分析:a=-1,表示bigdemical较小比bigdemical2;a=0,表示bigdemical等于bigdemical2;a=1表示bigdemical大于bigdemical2;例如:a大于等于bnewbigdemica(a).compareTo(newbigdemical(b))>=04.BigDecimal格式化因为NumberFormat类的format()方法可以使用BigDecimal对象作为参数,所以可以使用BigDecimal控制货币值超过16位有效数字、百分比值和一般值的格式。以使用BigDecimal格式化货币和百分比为例。首先创建一个BigDecimal对象,进行BigDecimal算术运算,分别建立货币和百分比格式化的引用,最后将BigDecimal对象作为format()方法的参数,输出其格式化后的货币值和百分比。NumberFormatcurrency=NumberFormat.getCurrencyInstance();//创建货币格式参考NumberFormatpercent=NumberFormat.getPercentInstance();//创建百分比格式参考percent.setMaximumFractionDigits(3);//百分比小数点最多3位BigDecimalloanAmount=newBigDecimal("15000.48");//贷款金额BigDecimalinterestRate=newBigDecimal("0.008");//利率BigDecimalinterest=loanAmount.multiply(interestRate);//乘以System.out.println("贷款金额:\t"+currency.format(loanAmount));System.out.println("利率:\t"+percent.format(interestRate));System.out.println("利息:\t"+currency.format(利息));结果:贷款金额:¥15,000.48利率:0.8%利息:¥120.00BigDecimal格式保留2位小数,不足补0:publicclassNumberFormat{publicstaticvoidmain(String[]s){System.out.println(formatToNumber(newBigDecimal("3.435")));System.out.println(formatToNumber(newBigDecimal(0)));System.out.println(formatToNumber(newBigDecimal("0.00")));System.out.println(formatToNumber(newBigDecimal("0.001")));System.out.println(格式tToNumber(newBigDecimal("0.006")));System.out.println(formatToNumber(newBigDecimal("0.206")));}/***@desc1.0~1BigDecimal小数,格式化0后丢失,直接在里面加0正面*2.如果传入的参数等于0,则直接返回字符串“0.00”*3。对于大于1的小数,直接格式化返回的字符串*@paramobj传入的小数*@return*/publicstaticStringformatToNumber(BigDecimalobj){DecimalFormatdf=newDecimalFormat("#.00");if(obj.compareTo(BigDecimal.ZERO)==0){return"0.00";}elseif(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(newBigDecimal(1))<0){return"0"+df.format(obj).toString();}else{returndf.format(obj).toString();}}}结果为:3.440.000.000.000.010.215.BigDecimal5.1的常见异常。除法时出现异常java.lang.ArithmeticException:Non-terminatingdecimalexpansion;noexactrepresentabledecimalresult原因分析:当除法不是通过BigDecimal的divide方法进行时,出现小数无限循环时,会抛出Exception:java.lang.ArithmeticException:Non-terminatingdecimalexpansion;没有可精确表示的十进制结果。解决方法:divide方法设置一个精确的小数点,如:divide(xxxxx,2)6.BigDecimal总结6.1.使用BigDecimal进行小数计算。BigDecimal的性能比double和float差,尤其是在处理大而复杂的运算时。因此,一般的精度计算没有必要使用BigDecimal。尝试使用参数类型为String的构造函数。BigDecimal是不可变的,每进行一次四次运算都会产生一个新的对象,所以做加减乘除时记得保存运算后的值。6.2.推荐工具包com.vivo.ars.util;importjava.math.BigDecimal;/***用于常用数学运算的高精度处理*/publicclassArithmeticUtils{//默认除法精度privatestaticfinalintDEF_DIV_SCALE=10;/***提供精确的加法运算**@paramv1summand*@paramv2summand*@return两个参数之和*/publicstaticdoubleadd(doublev1,doublev2){BigDecimalb1=newBigDecimal(Double.toString(v1));BigDecimalb2=newBigDecimal(Double.toString(v2));returnb1.add(b2).doubleValue();}/***提供精确加法运算**@paramv1求和*@paramv2求和*@返回两个参数的和*/publicstaticBigDecimaladd(Stringv1,Stringv2){BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);returnb1.add(b2);}/***提供精确加法运算**@paramv1summand*@paramv2addNumber*@paramscale保留小数位数*@return两个参数之和*/publicstaticStringadd(Stringv1,Stringv2,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);returnb1.add(b2).setScale(scale,BigDecimal.ROUND_HALF_UP).toString();}/***提供精确减法运算**@paramv1minuend*@paramv2subtrahend*@返回两个参数数字之间的区别*/publicstaticdoublesub(doublev1,doublev2){BigDecimalb1=newBigDecimal(Double.toString(v1));BigDecimalb2=newBigDecimal(Double.toString(v2));returnb1.subtract(b2).doubleValue();}/***提供精确减法**@paramv1减数*@paramv2减数*@return两个参数之间的差异*/publicstaticBigDecimalsub(Stringv1,Stringv2){BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);returnb1.subtract(b2);}/***提供精确的减法运算**@paramv1minuend*@paramv2subtrahend*@paramscalereservescaledigits*@return两个参数的差值*/publicstaticStringsub(Stringv1,Stringv2,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);returnb1.subtract(b2).setScale(scale,BigDecimal.ROUND_HALF_UP).to*String();}/**提供精确的乘法运算**@paramv1乘数*@paramv2乘数*@返回两个参数的乘积*/publicstaticdoublemul(doublev1,doublev2){BigDecimalb1=newBigDecimal(Double.toString(v1));BigDecimalb2=newBigDecimal(Double.toString(v2));returnb1.multiply(b2).doubleValue();}/***提供精确的乘法运算**@paramv1multiplier*@paramv2multiplier*@返回两个参数Product*/publicstaticBigDecimalmul(Stringv1,Stringv2){BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);returnb1.multiply(b2);}/***提供精确乘法**@paramv1被乘数*@paramv2乘数??@paramscale保留小数位数*@return两个参数的乘积*/publicstaticdoublemul(doublev1,doublev2,intscale){BigDecimalb1=newBigDecimal(Double.toString(v1));BigDecimalb2=newBigDecimal(Double.toString(v2));returnround(b1.multiply(b2).doubleValue(),scale);}/***提供精确乘法**@paramv1被乘数*@paramv2乘数??@paramscale保留小数位数*@返回两个参数的乘积*/publicstaticStringmul(Stringv1,Stringv2,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);returnb1.multiply(b2).setScale(scale,BigDecimal.ROUND_HALF_UP).toString();}/***提供(相对)精确除法运算,出现不可整除时,精确到*小数点后10位,后面的数字四舍五入**@paramv1被除数*@paramv2除数*@return两个参数的商*/publicstaticdoublediv(doublev1,doublev2){returndiv(v1,v2,DEF_DIV_SCALE);}/***提供(相对)精确的除法运算当出现不可分割的情况时,精度由scale参数指定,后面的数字四舍五入**@paramv1dividend*@paramv2divisor*@paramscale表示需要精确到小数点后几位。*@return两个参数的商*/publicstaticdoublediv(doublev1,doublev2,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb1=newBigDecimal(Double.toString(v1));BigDecimalb2=newBigDecimal(Double.toString(v2));返回b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();}/***提供(相对)精确的除法。当出现不可整除的情况时,精度由scale参数指定,后面的数字向上取整**@paramv1dividend*@paramv2divisor*@paramscale表示需要精确到小数点后几位*@return两个参数的商*/publicstaticStringdiv(Stringv1,Stringv2,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v1);returnb1.divide(b2,scale.UNDRO_FUPAL,BigDecimalFAL,BigDecimal).toString();}/***提供精确的小数位四舍五入**@paramv要四舍五入的数字*@paramscale保留小数点后几位*@return四舍五入的结果*/publicstaticdoubleround(doublev,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb=newBigDecimal(Double.toString(v));returnb.setScale(scale,BigDecimal.ROUND_HALF_UP).doubleValue();}/***提供准确的D小数位四舍五入处理**@paramv需要对数字进行四舍五入*@paramscale保留小数点后几位*@return四舍五入的结果*/publicstaticStringground(Stringv,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb=newBigDecimal(v);returnb.setScale(scale,BigDecimal.ROUND_HALF_UP).toString();}/***Remainder**@paramv1dividend*@paramv2divisor*@paramscale保留小数点后几位*@returnremainder*/publicstaticStringremainder(Stringv1,Stringv2,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);returnb1.remainder(b2).setScale(scale,BigDecimal.ROUND_HALF_UP}.toString();/***RemainderBigDecimal**@paramv1dividend*@paramv2divisor*@paramscale保留小数点后几位*@returnremainder*/publicstaticBigDecimalremainder(BigDecimalv1,BigDecimalv2,intscale){if(scale<0){thrownewIllegalArgumentException("Thescalemustbeapositiveintegerorzero");}returnv1.remainder(v2).setScale(scale,BigDecimal.ROUND_HALF_UP);}/***比较大小**@paramv1比较数*@paramv2比较数*@return如果v1大于v2,返回true否则返回false*/publicstaticbooleancompare(Stringv1,Stringv2){BigDecimalb1=newBigDecimal(v1);BigDecimalb2=newBigDecimal(v2);intbj=b1.compareTo(b2);booleanres;if(bj>0)res=true;elseres=false;returnres;}}