关于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
 
Java.math.BigDecimal.scale()方法实例
 
Java BigDecimal详解

谢谢!


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

Android BigDecimal对金额进行精确计算

但我们使用float或者Double对金额进行计算时,经常会出现精度丢失的情况,或者单我们要使用精确计算时,那就需要使用BigDecimal类来进行计算。...
  • a_kevin
  • a_kevin
  • 2017年07月18日 16:53
  • 1143

BigDecimal类的加减乘除(解决double计算精度问题)

BigDecimal简介 Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中...
  • qq_28270161
  • qq_28270161
  • 2015年07月09日 16:31
  • 2035

Java中BigDecimal精度丢失

doule不能表示为任何有限长度的二进制小数。 1.前言   Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。   双精度浮点型变量...
  • xiaozhushowtime
  • xiaozhushowtime
  • 2017年05月22日 21:15
  • 1738

java.math.BigDecimal的精度问题

1. String myMoney = "100.0128";  BigDecimal money= new BigDecimal(myMoney);  //设置精度,以及舍入规则  money= m...
  • suliqiang
  • suliqiang
  • 2006年05月08日 17:40
  • 13012

BigDecimal 保住数据精度

双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。Java在java.math包中提供的类BigDecimal,用来对超过16位有效位的数进行精确的...
  • qishuo_java
  • qishuo_java
  • 2014年09月01日 22:33
  • 3195

关于BigDecimal的使用

关于BigDecimal的使用
  • u011526599
  • u011526599
  • 2016年10月26日 00:09
  • 1139

BigDecimal 的那些坑事儿

最近查看rebate数据时,发现一个bug,主要现象是,当扣款支付宝的账号款项时,返回的是数字的金额为元,而数据库把金额存储为分,这中间要做元与分的转化,这个转化规则很简单,就是*100的,所以一开始...
  • ugg
  • ugg
  • 2012年11月22日 18:40
  • 39724

BigDecimal加减乘除运算

java.math.BigDecimal。BigDecimal一共有4个够造方法,让我先来看看其中的两种用法: 第一种:BigDecimal(double val) Translates a do...
  • huiwenjie168
  • huiwenjie168
  • 2011年11月21日 21:22
  • 257656

java BigDecimal 高精度运算 小数点处理详解

保留两位小数{ 方法一:{    double   c=3.154215;    java.text.DecimalFormat myformat=new java.text.Dec...
  • catkint
  • catkint
  • 2016年03月06日 21:19
  • 736

BigDecimal准确设置小数点后的精度

因为BigDecimal的原因吧,也可以说是double的问题吧 new   BigDecimal(currentLat2); 时值不再是 2.455675而是2.455674999999999...
  • a363722188
  • a363722188
  • 2015年06月25日 10:18
  • 7176
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于BigDecimal精度影响计算结果的问题
举报原因:
原因补充:

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