天天看点

BigDecimal代替浮点数精确计算用法简介

浮点数

浮点数是属于有理数中某特定子集的数的数字表示,在计算机中用以近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学计数法。

浮点计算是指浮点数参与的运算,这种运算通常伴随着因为无法精确表示而进行的近似或舍入。

一个浮点数a由两个数m和e来表示:a = m × b^e。在任意一个这样的系统中,我们选择一个基数b(记数系统的基)和精度p(即使用多少位来存储)。m(即尾数)是形如±d.ddd...ddd的p位数(每一位是一个介于0到b-1之间的整数,包括0和b-1)。如果m的第一位是非0整数,m称作规格化的。有一些描述使用一个单独的符号位(s 代表+或者-)来表示正负,这样m必须是正的。e是指数。

bigdecimal

在java中,float声明的变量是单精度浮点数,double声明的变量是双精度浮点数,顾名思义就是double型的实体占用内存空间是float的两倍。float是4个字节而double是8个字节。float和double类型的数据,无法精确表示计算结果,这是由于float和double是不精确的计算。大家可以通过下面代码可以看出来:

运行的结果为:

0.060000000000000005

0.5800000000000001

401.49999999999994

1.2329999999999999

要想获得理想的结果,我们可以考虑使用bigdecimal来获得更精确的计算:

bigdecimal提供了多个构造函数,和浮点数有关的有:

bigdecimal(double val)  translates a double into a bigdecimal.

bigdecimal(string val) translates the string repre sentation of a bigdecimal into a bigdecimal.

但是用double参数来创建对象得到不精确的值,只有通过string来创建对象才是最准确的。

例如:

bigdecimal data1=new bigdecimal(0.05);

system.out.println(data1.tostring());

bigdecimal data2=new bigdecimal("0.05");

system.out.println(data2.tostring());

得到结果:

0.05000000000000000277555756156289135105907917022705078125

所以,我们最终需要使用string来创建对象,这样得到的结果才是最精确的。另外,如果是double数,我们还可以使用:bigdecimal.valueof(double val),原因很简单,其jdk源码如下所示:

最后需要说明的是:bigdecimal的加减乘除其实最终都返回的是一个新的bigdecimal对象,因为bigdecimal是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以a.add(b);虽然做了加法操作,但是a并没有保存加操作后的值,正确的用法应该是a=a.add(b)。

参考文章:

<a href="http://swiftlet.net/archives/798" target="_blank">http://swiftlet.net/archives/798</a>