概述
在浮点运算中,浮点运算很少是精确的。虽然一些数字(譬如 0.5 )可以精确地表示为二进制(底数 2)小数(因为 0.5 等于 2 -1),但其它一些数字(譬如 0.1 )就不能精确的表示。因此,浮点运算可能导致舍入误差,产生的结果接近但不等于你可能希望的结果。比如:下面的代码运行结果的实际值为100958.34,而不是100000。
public static void main(String[] args) {
float f = 0.1f;
float sum = 0;
for( int i=0; i<1000000; i++)
{
sum += f;
}
System.out.println(sum);
}
类似的,.1*26相乘所产生的结果不等于.1自身加26次所得到的结果。当将浮点数强制转换成整数时,产生的舍入误差甚至更严重,因为强制转换成整数类型会舍弃非整数部分,甚至对于那些“看上去似乎”应该得到整数值的计算,也存在此类问题。例如下面这段代码:
public static void main(String[] args) {
double d = 29.0 * 0.01;
System.out.println(d);
System.out.println((int) (d * 100));
}
运行结果:
0.29
28
看到结果是不是非常差异,所以建议大家不要用float、double等浮点值表示精确值一些非整数值(如几美元和几美分这样的小数)需要很精确。浮点数不是精确值,所以使用它们会导致舍入误差。因此,使用浮点数来试图表示象货币量这样的精确数量不是一个好的想法。使用浮点数来进行美元和美分计算会得到灾难性的后果。浮点数最好用来表示像测量值这类数值,这类值从一开始就不怎么精确。
解决方案:
JDK开发人员在很早就遇到了这个问题,并在JDK1.3起给我们提供了一种新的处理精确值的类BigDecimal,BigDecimal是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示 BigDecimal,换算因子表示左移小数点多少位,从而得到所期望范围内的值BigDecimal 给我们提供了加、减、乘和除等算术运算,由于BigDecimal是一个类,而且对象是不可变,不像float、double是基本变量,所以计算后的结果将放入一个新BigDecimal对象中,所以使用BigDecimal会占用很大的开销,不适合大规模的数学计算。设计它的目的是用来精确地表示小数,所以我们可以用它来表示货币和金额的计算。
BigDecimal 用法:
BigDecimal构造方法:
BigDecimal
一共有4个构造方法
BigDecimal(int)
创建一个具有参数所指定整数值的对象。
BigDecimal(double)
创建一个具有参数所指定双精度值的对象。
BigDecimal(long)
创建一个具有参数所指定长整数值的对象。
BigDecimal(String)
创建一个具有参数所指定以字符串表示的数值的对象。
BigDecimal 的运算方式 不支持 + - * / 这类的运算 它有自己的运算方法:
BigDecimal add(BigDecimal augend) 加法运算
BigDecimal subtract(BigDecimal subtrahend) 减法运算
BigDecimal multiply(BigDecimal multiplicand) 乘法运算
BigDecimal divide(BigDecimal divisor) 除法运算
以第一个float精度错误的例子,用BigDecimal计算:
public static void main(String[] args) {
float f = 0.1f;
float sum = 0;
for( int i=0; i<1000000; i++)
{
sum += f;
}
System.out.println("float sum="+sum);
BigDecimal b1 = new BigDecimal(Double.toString(0.01));
BigDecimal total = new BigDecimal(Double.toString(0));
for( int i=0; i<1000000; i++)
{
total=total.add(b1);
}
System.out.println("BigDecimal total="+total);
}
结果:
float sum=100958.34
BigDecimal total=10000.00
我们从结果中可以看出使用float计算结果是有误差的,而使用BigDecimal计算结果是正确的。我们再看以第二个double精度错误的例子,用BigDecimal计算:
public static void main(String[] args) {
double d = 29.0 * 0.01;
System.out.println(d);
/***double计算**/
System.out.println(d * 100);
/***BigDecimal计算**/
BigDecimal b1 = new BigDecimal(Double.toString(d));
BigDecimal b2 = new BigDecimal(Double.toString(100));
System.out.println( b1.multiply(b2).doubleValue() );
}
结果:
0.29
28.999999999999996
29.0
从上面两个例子可以看出BigDecimal能够很好处理浮点数计算精度问题,但是性能方面比float、double低很多,但是为了提高计算的精确度,尤其是像处理金额,建议大家还是使用BigDecimal进行计算,避免造成损失。
结束语:
结束语是引用IBM文档库里面的一段话:"在Java程序中使用浮点数和小数充满着陷阱。浮点数和小数不象整数一样“循规蹈矩”,不能假定浮点计算一定产生整型或精确的结果,虽然它们的确“应该”那样做。最好将浮点运算保留用作计算本来就不精确的数值,譬如测量。如果需要表示定点数(譬如,几美元和几美分),则使用 BigDecimal 。"。
转载于:https://my.oschina.net/u/3083178/blog/873783
最后
以上就是专注信封为你收集整理的金融、支付行业的开发者不得不知道的float、double计算误差问题解决方案:的全部内容,希望文章能够帮你解决金融、支付行业的开发者不得不知道的float、double计算误差问题解决方案:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复