Java使用BigDecimal进行高精度计算的示例代码
首先看如下代码示例:
System.out.println(0.05+0.01); System.out.println(0.05-0.03); System.out.println(1.025*100); System.out.println(305.1/1000);
输出结果为:
0.060000000000000005
0.020000000000000004
102.49999999999999
0.30510000000000004
Java语言支持两种基本的浮点类型:float和double,以及与它们对应的包装类Float和Double。它们都依据IEEE754标准,该标准为32位浮点和64位双精度浮点二进制小数定义了二进制标准。
IEEE754用科学记数法以底数为2的小数来表示浮点数。IEEE浮点数用1位表示数字的符号,用8位来表示指数,用23位来表示尾数,即小数部分,作为有符号整数的指数可以有正负之分,小数部分用二进制(底数2)小数来表示
不要用浮点值表示精确值
一些非整数值(如几美元和几美分这样的小数)需要很精确。浮点数不是精确值,所以使用它们会导致舍入误差。因此,使用浮点数来试图表示象货币量这样的精确数量不是一个好的想法。使用浮点数来进行美元和美分计算会得到灾难性的后果。浮点数最好用来表示象测量值这类数值,这类值从一开始就不怎么精确。
使用BigDecimal
从JDK1.3起,Java开发人员就有了另一种数值表示法来表示非整数:BigDecimal。BigDecimal是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。
用于加、减、乘和除的方法给BigDecimal值提供了算术运算。由于BigDecimal对象是不可变的,这些方法中的每一个都会产生新的BigDecimal对象。因此,因为创建对象的开销,BigDecimal不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则BigDecimal可以很好地胜任该任务。
构造BigDecimal数
对于BigDecimal,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的String表示作为输入。要小心使用BigDecimal(double)构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或String的构造函数。
publicclassTest{
publicstaticvoidmain(String[]args){
//以双精度浮点数进行构造
BigDecimalbd1=newBigDecimal(0.5);
BigDecimalbd2=newBigDecimal(0.1);
System.out.println(bd1.add(bd2));
//以String类型进行构造
BigDecimalbd3=newBigDecimal("0.5");
BigDecimalbd4=newBigDecimal("0.1");
System.out.println(bd3.add(bd4));
}
}
输出结果为:
0.6000000000000000055511151231257827021181583404541015625
0.6
上面代码分别以
BigDecimal(doubleval) BigDecimal(Stringval)
不同的方式进行构造BigDecimal数,输出的结果是不一样的。
回到最开始的示例,提供工具类进行精确的浮点数运算,包括加减乘除和四舍五入。
importjava.math.BigDecimal;
publicclassArithUtil{
privatestaticfinalintDEF_DIV_SCALE=6;//默认除法运算精度
/**
*提供精确的加法运算。
*
*@paramv1被加数
*@paramv2加数
*@return两个参数的和
*/
publicstaticdoubleadd(doublev1,doublev2){
BigDecimalb1=newBigDecimal(Double.toString(v1));
BigDecimalb2=newBigDecimal(Double.toString(v2));
returnb1.add(b2).doubleValue();
}
/**
*提供精确的减法运算。
*
*@paramv1被减数
*@paramv2减数
*@return两个参数的差
*/
publicstaticdoublesub(doublev1,doublev2){
BigDecimalb1=newBigDecimal(Double.toString(v1));
BigDecimalb2=newBigDecimal(Double.toString(v2));
returnb1.subtract(b2).doubleValue();
}
/**
*提供精确的乘法运算。
*
*@paramv1被乘数
*@paramv2乘数
*@return两个参数的积
*/
publicstaticdoublemul(doublev1,doublev2){
BigDecimalb1=newBigDecimal(Double.toString(v1));
BigDecimalb2=newBigDecimal(Double.toString(v2));
returnb1.multiply(b2).doubleValue();
}
/**
*提供(相对)精确的除法运算,当发生除不尽的情况时,精确到小数点以后10位,以后的数字四舍五入。
*
*@paramv1被除数
*@paramv2除数
*@return两个参数的商
*/
publicstaticdoublediv(doublev1,doublev2){
returndiv(v1,v2,DEF_DIV_SCALE);
}
/**
*提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。
*
*@paramv1被除数
*@paramv2除数
*@paramscale表示表示需要精确到小数点以后几位。
*@return两个参数的商
*/
publicstaticdoublediv(doublev1,doublev2,intscale){
if(scale<0){
thrownewIllegalArgumentException(
"Thescalemustbeapositiveintegerorzero");
}
BigDecimalb1=newBigDecimal(Double.toString(v1));
BigDecimalb2=newBigDecimal(Double.toString(v2));
returnb1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
*提供精确的小数位四舍五入处理。
*
*@paramv需要四舍五入的数字
*@paramscale小数点后保留几位
*@return四舍五入后的结果
*/
publicstaticdoubleround(doublev,intscale){
if(scale<0){
thrownewIllegalArgumentException(
"Thescalemustbeapositiveintegerorzero");
}
BigDecimalb=newBigDecimal(Double.toString(v));
BigDecimalone=newBigDecimal("1");
returnb.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
结束语:
在Java程序中使用浮点数和小数充满着陷阱。浮点数和小数不象整数一样“循规蹈矩”,不能假定浮点计算一定产生整型或精确的结果,虽然它们的确“应该”那样做。最好将浮点运算保留用作计算本来就不精确的数值,譬如测量。如果需要表示定点数(譬如,几美元和几美分),则使用BigDecimal。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。