关于BigDecimal精度影响计算结果的问题

原创 2017年01月03日 20:55:25
因为double本身有数值范围的限制,在处理金钱等需要高精度的数据时,我们会考虑使用BigDecimal。但是在使用BigDecimal时,有很多需要我们注意的细节。本文主要针对精度问题来进行说明。

设置和获取Scale精度

我们可能都知道BigDecimal是有精度的,通过以下几个setScale()重载方法,可以对精度进行设置。
 
BigDecimal setScale(int newScale)Returns a BigDecimal whose scale is the specified value, and whose value is numerically equal to this BigDecimal's.
BigDecimal setScale(int newScale, int roundingMode)Returns a BigDecimal whose scale is the specified value, and whose unscaled value is determined by multiplying or dividing this BigDecimal's unscaled value by the appropriate power of ten to maintain its overall value.
BigDecimal setScale(int newScale, RoundingMode roundingMode)Returns a BigDecimal whose scale is the specified value, and whose unscaled value is determined by multiplying or dividing this BigDecimal's unscaled value by the appropriate power of ten to maintain its overall value.
 
通过scale()方法可以获取精度,很奇怪,不是getScale()。

int scale()Returns the scale of this BigDecimal.

默认精度是多少

上述的设置和获取不是我要说的重点,我想说的是默认精度是多少,我之前自然而然的以为BigDecimal能够满足金钱的高精度计算,它确实可以,但是默认精度问题可能会让你掉进大坑。
 
先举例,看看一下BigDecimal的精度各是多少:
System.out.println(new BigDecimal(1).scale());
System.out.println(new BigDecimal(1.0).scale());
System.out.println(new BigDecimal(1.00).scale());
System.out.println(new BigDecimal(1.1).scale());
System.out.println(new BigDecimal(1.11).scale());
System.out.println(new BigDecimal(1.1111).scale());
System.out.println(new BigDecimal(11.1).scale());
System.out.println(new BigDecimal(111.1).scale());
System.out.println(new BigDecimal(1111.1).scale());
System.out.println(new BigDecimal(11111.1).scale());
System.out.println(new BigDecimal("1").scale());
System.out.println(new BigDecimal("1.0").scale());
System.out.println(new BigDecimal("1.00").scale());
System.out.println(new BigDecimal("1.1").scale());
System.out.println(new BigDecimal("1.11").scale());
System.out.println(new BigDecimal(999999999999999.1).scale());
System.out.println(new BigDecimal(999999999999999.1));
System.out.println(new BigDecimal("9999999999999999999999999999999999999999.000000").scale());
System.out.println(new BigDecimal("9999999999999999999999999999999999999999.000000"));
System.out.println(new BigDecimal("9999999999999999999999999999999999999999.000000").divide(new BigDecimal("9"),RoundingMode.HALF_DOWN)));
下面是打印结果,对照看后面的注释部分。
System.out.println(new BigDecimal(1).scale()); // 0
System.out.println(new BigDecimal(1.0).scale()); // 0
System.out.println(new BigDecimal(1.00).scale()); // 0
System.out.println(new BigDecimal(1.1).scale()); // 51
System.out.println(new BigDecimal(1.11).scale()); // 52
System.out.println(new BigDecimal(1.1111).scale()); // 51
System.out.println(new BigDecimal(11.1).scale()); // 49
System.out.println(new BigDecimal(111.1).scale()); // 45
System.out.println(new BigDecimal(1111.1).scale()); // 41
System.out.println(new BigDecimal(11111.1).scale()); // 39
System.out.println(new BigDecimal("1").scale()); // 0
System.out.println(new BigDecimal("1.0").scale()); // 1
System.out.println(new BigDecimal("1.00").scale()); // 2
System.out.println(new BigDecimal("1.1").scale()); // 1
System.out.println(new BigDecimal("1.11").scale()); // 2
System.out.println(new BigDecimal(999999999999999.1).scale()); // 3
System.out.println(new BigDecimal(999999999999999.1)); // 999999999999999.125
System.out.println(new BigDecimal("9999999999999999999999999999999999999999.000000").scale()); // 6
System.out.println(new BigDecimal("9999999999999999999999999999999999999999.000000")); // 9999999999999999999999999999999999999999.000000
System.out.println(new BigDecimal("9999999999999999999999999999999999999999.000000").divide(new BigDecimal("9"),RoundingMode.HALF_DOWN))); // 1111111111111111111111111111111111111111.000000


根据上面的打印结果来进行分析:
     1) 对于整型,精度就是0
     2) 对于double型
          小数点后面是0的话,精度就是0;
          小数点后面不是0,则取决于整数位的位数,整数位越多,精度越小;当整数位接近15位时,精度就趋于0,再多1位数据可能开始溢出(具体情况取决于double类型的数值表示)。
     3) 对于字符串类型的构造函数,小数点后面有几位,精度就是几位,而且几乎没有位数限制(试了40位都没有问题)

总结

尽量使用字符串来构建BigDecimal对象,这样不会受到到double本身取值范围限制的影响。
System.out.println(new BigDecimal("1.000000").scale());
或者可以在初始化BigDecimal对象后,显示为设置一次精度。
BigDecimal orderAmount = new BigDecimal(0);
orderAmount = orderAmount.setScale(6, RoundingMode.HALF_EVEN);
另外,BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,在运算时(包括setScale)千万要保存操作后的值。往往我们会使用连续调用,最后保存一次值。
BigDecimal orderMonthAmount; // 省略orderMonthAmount的初始化
BigDecimal orderValue = orderMonthAmount.divide(new BigDecimal(30), RoundingMode.HALF_EVEN)
                   .multiply(new BigDecimal(interval))
                   .setScale(2, BigDecimal.ROUND_HALF_EVEN);


参考

BigDecimal API
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
 
Java.math.BigDecimal.scale()方法实例
http://www.yiibai.com/java/math/bigdecimal_scale.html
 
Java BigDecimal详解
http://blog.csdn.net/jackiehff/article/details/8582449

谢谢!

http://www.alanzeng.cn/2017/01/bigdecimal-sacle/

版权声明:本文为博主原创文章,转载请注明出处。

相关文章推荐

BigDecimal.setScale()方法用于商业计算的精度设置问题详解

BigDecimal.setScale()方法用于商业计算的精度设置问题详解     网上的说法繁杂,看起来诸多不便,并且有的说法也不太准确,在这里 做一下求证。用的较多的4个参数: Bi...

【Java】解决计算浮点数精度问题(BigDecimal)

工具类如下: import java.math.BigDecimal; /** * * @ClassName: ArithUtils * @Description: 数学计算工...
  • hj7jay
  • hj7jay
  • 2016年09月05日 22:13
  • 1299

Java浮点数float,bigdecimal和double精确计算的精度误差问题总结

1、float整数计算误差 案例:会员积分字段采用float类型,导致计算会员积分时,7位整数的数据计算结果出现误差。 原因:超出float精度范围,无法精确计算。 float和double的精...
  • ShareUs
  • ShareUs
  • 2016年05月17日 15:48
  • 3403

数值计算精度问题(double,float,Bigdecimal)

java提供了一种能够精确计算小数的Bigdecimal类,通过构造方法new Bigdecimal(String)来得到精确的结果!...
  • winy_lm
  • winy_lm
  • 2015年12月16日 10:41
  • 532

java中计算精度问题的解决--BigDecimal类的使用

今天在学习的时候,第一次使用到BigDecimal类,特此记之。 首先很多人在利用java进行计算的时候经常会存在这样一个问题:package calculate; import java.math...

java.math.BigDecimal类的用法 解决double计算精度问题

在java中提供了大数字的操作类,即java.math.BinInteger类和java.math.BigDecimal类。这两个类用于高精度计 算,其中BigInteger类是针对大整数的处理类,而...

动态函数调用实现下列操作,输入2个数以及操作符计算结果@ 求最大公约数 $求最小公倍数 - 求差 + 求和 等等

//求最大公约数 int greatestDivisorOfTwo(int x, int y){ int min = x < y ? x : y; int i = 0; for...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于BigDecimal精度影响计算结果的问题
举报原因:
原因补充:

(最多只允许输入30个字)