当前位置 博文首页 > 一条IT:【BigDecimal】大数的精确计算你还在用Double?
嗨,大家好,我是一条。
感谢大家的支持,一条的原创文章关于debug你可能还不知道的技巧,建议所有人都看一下入选工具类排行榜第六名。
这是第一次入榜,写作的动力更足了。
为了让更多的人看到一条的分享,一条准备报名原力计划,报名条件是粉丝数超过2000。
所以一条现在非常需要大家的关注,如果觉得一条写的还可以,就点个关注再走吧!
等粉丝数达到2000时,一条给大家在微信准备一个抽奖,奖品暂定键盘和手环二选一,关注微信公众号就可以参与。
?
今天组长突然在群里@我,我当时就觉得情况不妙,果然
“以后涉及金额类的大数都要用BigDecimal,不要用Double”
“好的组长”
……
BigDecimal是什么东西,该怎么用呢
别急,一条这就和大家聊聊。
BigDecimal是Java在java.math包中提供的API类,用来对超过16位有效位的数进行精确的运算。是不是很简单,那么问题来了
一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,比如商业计算,则必须使用BigDecimal类来操作。Float和Double只能用来做科学计算或者是工程计算。
BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
主要是以下几种操作,也是本文的主要内容:
?
加下来一条就从新建到使用带大家玩转BigDecimal,保证你的组长不会说你。冲冲冲!
前面说BigDecimal所创建的是对象,提到对象毫无疑问new就完了
BigDecimal num1 = new BigDecimal(0.005);
//尽量用字符串的形式初始化
BigDecimal num22 = new BigDecimal("1000000");
?
对象的加减运算用 + - * / 肯定是不管用了,不过别慌,我们有方法。
参数类型为double的构造方法的结果有一定的不可预知性。其实在Java中写入newBigDecimal(0.1)所创建的BigDecimal实际上等于
0.1000000000000000055511151231257827021181583404541015625。
这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
//加法
BigDecimal result1 = num1.add(num2);
System.out.println(result1);
//如果num1用数字创建,结果为:
//1000000.005000000000000000104083408558608425664715468883514404296875
//如果num1用字符串创建,结果为:
//1000000.005
//减法
BigDecimal result2 = num1.subtract(num2);
//乘法
BigDecimal result3 = num1.multiply(num2);
//绝对值
BigDecimal result4 = num1.abs();
//除法
BigDecimal result5 = num2.divide(num1,20,BigDecimal.ROUND_HALF_UP);
System.out.println(result5);
//结果为:
//200000000.00000000000000000000
//20代表保留小数位数
//ROUND_HALF_UP代表四舍五入
?
一下八种模式皆用1/3举例,保留三位小数,结果对应写在后面
1、ROUND_UP:始终对非零舍弃部分前面的数字加1,即始终进位。—— 0.334
2、ROUND_DOWN:从不对舍弃部分前面的数字加1,即始终舍弃,相当于截断。 —— 0.333
3、ROUND_CEILING:如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;如果为负,则舍入行为与 ROUND_DOWN 相同。——0.334
4、ROUND_FLOOR:如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;如果为负,则舍入行为与 ROUND_UP 相同。——0.333
5、ROUND_HALF_UP:如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(四舍五入)。——0.333
6、ROUND_HALF_DOWN:如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。——0.333
7、ROUND_HALF_EVEN:如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。——0.334
注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。
四舍六入,五分两种情况。如果前一位为奇数,则入位,否则舍去。
8、ROUND_UNNECESSARY:断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。
?
主要是和String和其他基本类型的转换
toString()? ?将BigDecimal对象中的值转换成字符串
doubleValue()? ? 将BigDecimal对象中的值转换成双精度数
floatValue()? ?将BigDecimal对象中的值转换成单精度数
longValue()? ?将BigDecimal对象中的值转换成长整数
intValue()? ? 将BigDecimal对象中的值转换成整数?
由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。
NumberFormat?currency?=?NumberFormat.getCurrencyInstance();?//建立货币格式化引用?
NumberFormat?percent?=?NumberFormat.getPercentInstance();??//建立百分比格式化引用?
percent.setMaximumFractionDigits(3);?//百分比小数点最多3位?
BigDecimal?loanAmount?=?new?BigDecimal("15000.48");?//贷款金额
BigDecimal?interestRate?=?new?BigDecimal("0.008");?//利率???
BigDecimal?interest?=?loanAmount.multiply(interestRate);?//相乘
System.out.println("贷款金额:\t"?+?currency.format(loanAmount));?
System.out.println("利率:\t"?+?percent.format(interestRate));?
System.out.println("利息:\t"?+?currency.format(interest));?
java中对BigDecimal比较大小一般用的是bigdemical的compareTo方法
int?a?=?bigdemical.compareTo(BigDecimal.ZERO)
//结果分析
//a = -1,表示bigdemical小于0;
//a = 0,表示bigdemical等于0;
//a = 1,表示bigdemical大于0;
通过BigDecimal的divide方法进行除法时当不整除,出现无限循环小数时,就会抛异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
解决方法:
divide方法设置精确的小数点,如:divide(xxxxx,2)
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result
?
import?java.math.BigDecimal;
/**
?*?用于高精确处理常用的数学运算
?*/
public?class?ArithmeticUtils?{
????//默认除法运算精度
????private?static?final?int?DEF_DIV_SCALE?=?10;
????/**
?????*?提供精确的加法运算
?????*
?????*?@param?v1?被加数
?????*?@param?v2?加数
?????*?@return?两个参数的和
?????*/
????public?static?double?add(double?v1,?double?v2)?{
????????BigDecimal?b1?=?new?BigDecimal(Double.toString(v1));
????????BigDecimal?b2?=?new?BigDecimal(Double.toString(v2));
????????return?b1.add(b2).doubleValue();
????}
????/**
?????*?提供精确的加法运算
?????*
?????*?@param?v1?被加数
?????*?@param?v2?加数
?????*?@return?两个参数的和
?????*/
????public?static?BigDecimal?add(String?v1,?String?v2)?{
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????return?b1.add(b2);
????}
????/**
?????*?提供精确的加法运算
?????*
?????*?@param?v1????被加数
?????*?@param?v2????加数
?????*?@param?scale?保留scale?位小数
?????*?@return?两个参数的和
?????*/
????public?static?String?add(String?v1,?String?v2,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????"The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????return?b1.add(b2).setScale(scale,?BigDecimal.ROUND_HALF_UP).toString();
????}
????/**
?????*?提供精确的减法运算
?????*
?????*?@param?v1?被减数
?????*?@param?v2?减数
?????*?@return?两个参数的差
?????*/
????public?static?double?sub(double?v1,?double?v2)?{
????????BigDecimal?b1?=?new?BigDecimal(Double.toString(v1));
????????BigDecimal?b2?=?new?BigDecimal(Double.toString(v2));
????????return?b1.subtract(b2).doubleValue();
????}
????/**
?????*?提供精确的减法运算。
?????*
?????*?@param?v1?被减数
?????*?@param?v2?减数
?????*?@return?两个参数的差
?????*/
????public?static?BigDecimal?sub(String?v1,?String?v2)?{
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????return?b1.subtract(b2);
????}
????/**
?????*?提供精确的减法运算
?????*
?????*?@param?v1????被减数
?????*?@param?v2????减数
?????*?@param?scale?保留scale?位小数
?????*?@return?两个参数的差
?????*/
????public?static?String?sub(String?v1,?String?v2,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????"The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????return?b1.subtract(b2).setScale(scale,?BigDecimal.ROUND_HALF_UP).toString();
????}
????/**
?????*?提供精确的乘法运算
?????*
?????*?@param?v1?被乘数
?????*?@param?v2?乘数
?????*?@return?两个参数的积
?????*/
????public?static?double?mul(double?v1,?double?v2)?{
????????BigDecimal?b1?=?new?BigDecimal(Double.toString(v1));
????????BigDecimal?b2?=?new?BigDecimal(Double.toString(v2));
????????return?b1.multiply(b2).doubleValue();
????}
????/**
?????*?提供精确的乘法运算
?????*
?????*?@param?v1?被乘数
?????*?@param?v2?乘数
?????*?@return?两个参数的积
?????*/
????public?static?BigDecimal?mul(String?v1,?String?v2)?{
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????return?b1.multiply(b2);
????}
????/**
?????*?提供精确的乘法运算
?????*
?????*?@param?v1????被乘数
?????*?@param?v2????乘数
?????*?@param?scale?保留scale?位小数
?????*?@return?两个参数的积
?????*/
????public?static?double?mul(double?v1,?double?v2,?int?scale)?{
????????BigDecimal?b1?=?new?BigDecimal(Double.toString(v1));
????????BigDecimal?b2?=?new?BigDecimal(Double.toString(v2));
????????return?round(b1.multiply(b2).doubleValue(),?scale);
????}
????/**
?????*?提供精确的乘法运算
?????*
?????*?@param?v1????被乘数
?????*?@param?v2????乘数
?????*?@param?scale?保留scale?位小数
?????*?@return?两个参数的积
?????*/
????public?static?String?mul(String?v1,?String?v2,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????"The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????return?b1.multiply(b2).setScale(scale,?BigDecimal.ROUND_HALF_UP).toString();
????}
????/**
?????*?提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
?????*?小数点以后10位,以后的数字四舍五入
?????*
?????*?@param?v1?被除数
?????*?@param?v2?除数
?????*?@return?两个参数的商
?????*/
????public?static?double?div(double?v1,?double?v2)?{
????????return?div(v1,?v2,?DEF_DIV_SCALE);
????}
????/**
?????*?提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
?????*?定精度,以后的数字四舍五入
?????*
?????*?@param?v1????被除数
?????*?@param?v2????除数
?????*?@param?scale 表示表示需要精确到小数点以后几位。
?????*?@return?两个参数的商
?????*/
????public?static?double?div(double?v1,?double?v2,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException("The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b1?=?new?BigDecimal(Double.toString(v1));
????????BigDecimal?b2?=?new?BigDecimal(Double.toString(v2));
????????return?b1.divide(b2,?scale,?BigDecimal.ROUND_HALF_UP).doubleValue();
????}
????/**
?????*?提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
?????*?定精度,以后的数字四舍五入
?????*
?????*?@param?v1????被除数
?????*?@param?v2????除数
?????*?@param?scale?表示需要精确到小数点以后几位
?????*?@return?两个参数的商
?????*/
????public?static?String?div(String?v1,?String?v2,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException("The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v1);
????????return?b1.divide(b2,?scale,?BigDecimal.ROUND_HALF_UP).toString();
????}
????/**
?????*?提供精确的小数位四舍五入处理
?????*
?????*?@param?v?????需要四舍五入的数字
?????*?@param?scale?小数点后保留几位
?????*?@return?四舍五入后的结果
?????*/
????public?static?double?round(double?v,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException("The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b?=?new?BigDecimal(Double.toString(v));
????????return?b.setScale(scale,?BigDecimal.ROUND_HALF_UP).doubleValue();
????}
????/**
?????*?提供精确的小数位四舍五入处理
?????*
?????*?@param?v?????需要四舍五入的数字
?????*?@param?scale?小数点后保留几位
?????*?@return?四舍五入后的结果
?????*/
????public?static?String?round(String?v,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????"The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b?=?new?BigDecimal(v);
????????return?b.setScale(scale,?BigDecimal.ROUND_HALF_UP).toString();
????}
????/**
?????*?取余数
?????*
?????*?@param?v1????被除数
?????*?@param?v2????除数
?????*?@param?scale?小数点后保留几位
?????*?@return?余数
?????*/
????public?static?String?remainder(String?v1,?String?v2,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????"The?scale?must?be?a?positive?integer?or?zero");
????????}
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????return?b1.remainder(b2).setScale(scale,?BigDecimal.ROUND_HALF_UP).toString();
????}
????/**
?????*?取余数??BigDecimal
?????*
?????*?@param?v1????被除数
?????*?@param?v2????除数
?????*?@param?scale?小数点后保留几位
?????*?@return?余数
?????*/
????public?static?BigDecimal?remainder(BigDecimal?v1,?BigDecimal?v2,?int?scale)?{
????????if?(scale?<?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????"The?scale?must?be?a?positive?integer?or?zero");
????????}
????????return?v1.remainder(v2).setScale(scale,?BigDecimal.ROUND_HALF_UP);
????}
????/**
?????*?比较大小
?????*
?????*?@param?v1?被比较数
?????*?@param?v2?比较数
?????*?@return?如果v1?大于v2?则?返回true?否则false
?????*/
????public?static?boolean?compare(String?v1,?String?v2)?{
????????BigDecimal?b1?=?new?BigDecimal(v1);
????????BigDecimal?b2?=?new?BigDecimal(v2);
????????int?bj?=?b1.compareTo(b2);
????????boolean?res;
????????if?(bj?>?0)
????????????res?=?true;
????????else
????????????res?=?false;
????????return?res;
????}
}
BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal,在需要精确的小数计算时再使用BigDecimal。
尽量使用参数类型为String的构造函数。
BigDecimal都是不可变的,类似String。 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。
?
我是一条,一个在互联网摸爬滚打的程序员。
微信关注【一条IT】,第一时间获取文章推送。
道阻且长,行则将至。大家的?【点赞,收藏,关注】?就是一条创作的最大动力,我们下期见!
注:关于本篇博客有任何问题和建议,欢迎大家留言!
?
cs