0 0

BigDecimal之性能问题10

 

 

我们知道浮点数存在精度丢失问题。(精度为什么会丢失,本人之前做过一个总结,可以参考:java 浮点数为什么精度会丢失
为了精度问题,我们常常不直接使用浮点数,常常是采用BigDecimal来替换浮点数。本文主要想探索一下BigDecimal相比于double性能上可能存在的一些问题。


先说一下现象:本人在实现一个算法的时候(算法运算量十分巨大),为了避免精度丢失,使用了BigDecimal,发现运算时间是几个小时。而换做double之后才花了将近10钟。这么大的差距难于置信!

于是写了一个简单的测试程序如下:(注:以下测试为了纯测试和比较!)

import java.math.BigDecimal;

public class BigDecimalEfficiency {

	public static int REPEAT_TIMES = 1000000;

	public static double computeByBigDecimal(double a, double b) {
		BigDecimal result = BigDecimal.valueOf(0);
		BigDecimal decimalA = BigDecimal.valueOf(a);
		BigDecimal decimalB = BigDecimal.valueOf(b);
		for (int i = 0; i < REPEAT_TIMES; i++) {
			result = result.add(decimalA.multiply(decimalB));
		}
		return result.doubleValue();
	}

	public static double computeByDouble(double a, double b) {
		double result = 0;
		for (int i = 0; i < REPEAT_TIMES; i++) {
			result += a * b;
		}
		return result;
	}

	public static void main(String[] args) {
		long test = System.nanoTime();
		long start1 = System.nanoTime();
		double result1 = computeByBigDecimal(0.120000000034, 11.22);
		long end1 = System.nanoTime();
		long start2 = System.nanoTime();
		double result2 = computeByDouble(0.120000000034, 11.22);
		long end2 = System.nanoTime();

		long timeUsed1 = (end1 - start1);
		long timeUsed2 = (end2 - start2);
		System.out.println("result by BigDecimal:" + result1);
		System.out.println("time used:" + timeUsed1);
		System.out.println("result by Double:" + result2);
		System.out.println("time used:" + timeUsed2);

		System.out.println("timeUsed1/timeUsed2=" + timeUsed1 / timeUsed2);
	}
}
 

运行结果如下:


result by BigDecimal:1346400.00038148
time used:365847335
result by Double:1346400.000387465
time used:5361855
timeUsed1/timeUsed2=68

从结果上来看BigDecimal给我们带来了精度上的提升,但是性能上的损耗是巨大的。同样的运算时间居然是double的68倍。

也难怪我的那个算法,double几分钟就搞定了,用BigDecimal花了几个小时。


不知道以上的测试结果靠谱吗????

还是我对BigDecimal使用有误?

 


问题补充
yangyi 写道
是不是应该取到double的max_value, 然后分块计算,再用bigdecimal合并比较好


分块计算 这个还不是很明白 没试过? 能否再说说?

问题补充
半人马 写道
我其实一直很好奇什么样的货币类计算需要使用BigDecimal。大部分跟钱相关的应用,最终都会指定一个舍入精度。在我接触到的一些消费型应用,支付型应用中,这个舍入精度都是“分”而已,所以我选择使用long来做计算。

货币用long是没错,我们目前项目里也是这么用的!
我这个场景不是货币,哈哈
问题补充
CN_DOTA_HOPE 写道
在慢能慢到那去...
实际应用中也不可能像LZ那样 一下计算那么多次吧

哈哈 看场景
一般应用是可以忽略的
但是这个性能差距一定要心里有数
问题补充
coffeesweet 写道
麻烦哪位能具体给举个double转long来计算的例子???
计算完成后的结果怎么处理呢??
谢谢!

应该不是说double就能用long来表示。
而是要看场景,比如或货币运算,如果精确到分,那最多只有2位小数。这个时候对double乘于100就能用long来表示了。只是数字的单位是分。

我们目前项目里关于金钱是这样来表示,都是以分为单位,用long来表示,数据库也是。前台展示的时候,再把分转换成元。这样为的是在系统内部避免了使用double来运算。
2011年4月13日 07:55

23个答案 按时间排序 按投票排序

0 0

1、如果使用long,那象利息计算等需要高精度数据时怎么处理?
2、别说数据量不大哦,现在大的银行有上亿个账户不算多吧
这个怎么办?

2011年4月13日 07:55
0 0

魔力猫咪 写道
4.8/6试试,出来的结果是0.7999999999999999。出现这种滑稽的计算结果是因为浮点数在计算机中的保存方式造成的。在特定的数值处理后就会出现这种误差。
BigDecimal主要是为了解决这类问题和舍入而设计的。而且精确计算必须使用String作为参数创建才可以。
对于要求精确,并且计算不复杂的情况下,使用BigDecimal是一个正确的选择。如果没有这种精确要求,或者计算非常庞大,那么就要考虑是否使用其他方式了。
在目前的Java应用系统开发中,大多数这种计算都是不是非常复杂的货币计算,所以大家都使用BigDecimal处理。

是,前一段java解惑就是这么说的,不过上面也说了可以用long来解决

2011年4月13日 07:55
0 0

4.8/6试试,出来的结果是0.7999999999999999。出现这种滑稽的计算结果是因为浮点数在计算机中的保存方式造成的。在特定的数值处理后就会出现这种误差。
BigDecimal主要是为了解决这类问题和舍入而设计的。而且精确计算必须使用String作为参数创建才可以。
对于要求精确,并且计算不复杂的情况下,使用BigDecimal是一个正确的选择。如果没有这种精确要求,或者计算非常庞大,那么就要考虑是否使用其他方式了。
在目前的Java应用系统开发中,大多数这种计算都是不是非常复杂的货币计算,所以大家都使用BigDecimal处理。

2011年4月13日 07:55
0 0

凤舞凰扬 写道
mtnt2008 写道
魔力猫咪 写道
BigDecimal解决的是精度问题,主要用于货币类的需要高精度的数值计算。快速科学计算应该使用double,BigDecimal本来就不是用来进行科学计算的。


+1

   什么样的货币计算需要用到bigdecimal?而且还有人正解,支持......
   其实在绝大多数的业务应用场景下,double完全足够,bigdecimal应用场景并不广泛。(当然了,用java做数值计算是另外一回事了,但似乎很少)精度有一个是累积精度问题,有一个是计算精度的问题。
   精度在应用问题上很大一个原因是大家没有用好或者说没有分析好场景,或者说没有理解精度的适应性问题。举个简单例子,假如有一个工作,是计算一个人在一段时间范围内,挖土的工作量产生的费用。假设,计费单位是小数点3位,时间是小数点2为,工作量(比如土石方)是长,宽,高(小数点各3位),那么计算费用,大家会如何算?
   不太动脑筋的人肯定是 费用×时间×长×宽×高,再一看,好像精度有丢失哦,double不行,直接用bigdecimal了。仔细动一下脑筋,有必要么?
   结果值很简单,也就是费用,费用2位精度,3位有效数足以。极端点也就是4,5位了。那么这个计算方式,应该是体积=长×宽×高,然后保留3位精度(其实这个数值也是需要体现的),最后再计算费用了。

   再回头看看我们实际的应用场景,真的精度一定要达到小数点后7,8位么?如果是,何不考虑把单位放小(那些采用long来处理货币的就是一个极端的放小例子了)。

我们计算银行的利润分配的时候基本用bigdecimal,精确到小数后6位,呵呵,不过确实用的很少,一般大部分都还是用long,显示的时候直接转换/100

2011年4月13日 07:55
0 0

魔力猫咪 写道
BigDecimal解决的是精度问题,主要用于货币类的需要高精度的数值计算。快速科学计算应该使用double,BigDecimal本来就不是用来进行科学计算的。

汇率问题,还有一些金融类的交易询价都是需要高精度的地方

2011年4月13日 07:55
0 0

linliangyi2007 写道
大型科学计算貌似不使用java做主要语言吧,这个根本性问题请楼主注意啊。
Ada,FORTRAN语言视乎更适合高精度计算领域。

恩 是滴! BigDecimal做金融的钱币计算就够了!

2011年4月13日 07:55
0 0

这么多人讨论这个精度问题,为啥不去买一本计算数学,到底误差是多少,大多数计算公式都可以算出误差范围的。在这讨论半天都是一鳞半爪,以偏概全。

2011年4月13日 07:55
0 0

凤舞凰扬 写道
mtnt2008 写道
魔力猫咪 写道
BigDecimal解决的是精度问题,主要用于货币类的需要高精度的数值计算。快速科学计算应该使用double,BigDecimal本来就不是用来进行科学计算的。


+1

   什么样的货币计算需要用到bigdecimal?而且还有人正解,支持......
   其实在绝大多数的业务应用场景下,double完全足够,bigdecimal应用场景并不广泛。(当然了,用java做数值计算是另外一回事了,但似乎很少)精度有一个是累积精度问题,有一个是计算精度的问题。
   精度在应用问题上很大一个原因是大家没有用好或者说没有分析好场景,或者说没有理解精度的适应性问题。举个简单例子,假如有一个工作,是计算一个人在一段时间范围内,挖土的工作量产生的费用。假设,计费单位是小数点3位,时间是小数点2为,工作量(比如土石方)是长,宽,高(小数点各3位),那么计算费用,大家会如何算?
   不太动脑筋的人肯定是 费用×时间×长×宽×高,再一看,好像精度有丢失哦,double不行,直接用bigdecimal了。仔细动一下脑筋,有必要么?
   结果值很简单,也就是费用,费用2位精度,3位有效数足以。极端点也就是4,5位了。那么这个计算方式,应该是体积=长×宽×高,然后保留3位精度(其实这个数值也是需要体现的),最后再计算费用了。

   再回头看看我们实际的应用场景,真的精度一定要达到小数点后7,8位么?如果是,何不考虑把单位放小(那些采用long来处理货币的就是一个极端的放小例子了)。


我们在实际项目应用中,为了保证中间计算的精确,一般都使用BigDecimal,数据库中也都是number(32,18)这样的存储,用double计算出来的值会存在尾差

2011年4月13日 07:55
0 0

麻烦哪位能具体给举个double转long来计算的例子???
计算完成后的结果怎么处理呢??
谢谢!

2011年4月13日 07:55
0 0

看看BigDecimal的实现吧

2011年4月13日 07:55
0 0

在慢能慢到那去...
实际应用中也不可能像LZ那样 一下计算那么多次吧

2011年4月13日 07:55
0 0

gtssgtss 写道
比如:用通项公式计算斐波那契数列,用double很快就和递推公式的结果产生差异。。。。。

   用double做数值计算,在作为证据说明Bigdecimal? 呵呵,不带这样的啊~~~~

2011年4月13日 07:55
0 0

比如:用通项公式计算斐波那契数列,用double很快就和递推公式的结果产生差异。。。。。

2011年4月13日 07:55
0 0

mtnt2008 写道
魔力猫咪 写道
BigDecimal解决的是精度问题,主要用于货币类的需要高精度的数值计算。快速科学计算应该使用double,BigDecimal本来就不是用来进行科学计算的。


+1

   什么样的货币计算需要用到bigdecimal?而且还有人正解,支持......
   其实在绝大多数的业务应用场景下,double完全足够,bigdecimal应用场景并不广泛。(当然了,用java做数值计算是另外一回事了,但似乎很少)精度有一个是累积精度问题,有一个是计算精度的问题。
   精度在应用问题上很大一个原因是大家没有用好或者说没有分析好场景,或者说没有理解精度的适应性问题。举个简单例子,假如有一个工作,是计算一个人在一段时间范围内,挖土的工作量产生的费用。假设,计费单位是小数点3位,时间是小数点2为,工作量(比如土石方)是长,宽,高(小数点各3位),那么计算费用,大家会如何算?
   不太动脑筋的人肯定是 费用×时间×长×宽×高,再一看,好像精度有丢失哦,double不行,直接用bigdecimal了。仔细动一下脑筋,有必要么?
   结果值很简单,也就是费用,费用2位精度,3位有效数足以。极端点也就是4,5位了。那么这个计算方式,应该是体积=长×宽×高,然后保留3位精度(其实这个数值也是需要体现的),最后再计算费用了。

   再回头看看我们实际的应用场景,真的精度一定要达到小数点后7,8位么?如果是,何不考虑把单位放小(那些采用long来处理货币的就是一个极端的放小例子了)。

2011年4月13日 07:55
0 0

之前使用BigDecimal主要就是解决浮点数精度丢失问题,根本没有考虑到性能上的问题,看来以后得注意了,O(∩_∩)O~。

2011年4月13日 07:55
0 0

大型科学计算貌似不使用java做主要语言吧,这个根本性问题请楼主注意啊。
Ada,FORTRAN语言视乎更适合高精度计算领域。

2011年4月13日 07:55
0 0

我其实一直很好奇什么样的货币类计算需要使用BigDecimal。大部分跟钱相关的应用,最终都会指定一个舍入精度。在我接触到的一些消费型应用,支付型应用中,这个舍入精度都是“分”而已,所以我选择使用long来做计算。

2011年4月13日 07:55
0 0

魔力猫咪 写道
BigDecimal解决的是精度问题,主要用于货币类的需要高精度的数值计算。快速科学计算应该使用double,BigDecimal本来就不是用来进行科学计算的。


+1

2011年4月13日 07:55
0 0

魔力猫咪 写道
BigDecimal解决的是精度问题,主要用于货币类的需要高精度的数值计算。快速科学计算应该使用double,BigDecimal本来就不是用来进行科学计算的。

正解。。

2011年4月13日 07:55
0 0

BigDecimal解决的是精度问题,主要用于货币类的需要高精度的数值计算。快速科学计算应该使用double,BigDecimal本来就不是用来进行科学计算的。

2011年4月13日 07:55
0 0

yangyi 写道
是不是应该取到double的max_value, 然后分块计算,再用bigdecimal合并比较好


这个只能解决大数问题,解决不了精度问题啊

2011年4月13日 07:55
0 0

是不是应该取到double的max_value, 然后分块计算,再用bigdecimal合并比较好

2011年4月13日 07:55
0 0

由于以前项目用一个开源公式计算类库,里面使用BigDecimal装数据,效率的确很差
采取大量数据随机计算比对过,在数据类型这块带来的差距大约也是50倍左右,对象构造成本太大

2011年4月13日 07:55

相关推荐

Global site tag (gtag.js) - Google Analytics