您的位置:首页 > 其它

如果要求精确的答案,请避免使用float和double

2013-11-19 19:29 375 查看
floatdouble类型的主要设计目的是为了科学计算和工程计算。它们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。因为要让一个float或者double精确地表示0.1(或者10的任何负数次方值)是不可能的,float和double类型对于货币计算尤为不合适,因为要让一个float或double精确地表示0.1或10的任何其他负数次方值是不可能的。比如System.out.println(2.0-1.1)将会打印0.899999999999999,而不是你所希望的0.9,这种舍入错误产生的原因是浮点数实际上是用二进制系统实现的,而分数1/10在二进制系统中没有精确的表示,其道理就如同在十进制系统中无法精确表示1/3一样;再比如0.5在二进制系统中有精确表示,而0.55在二进制系统中没有精确表示。

System.out.println(1.03 – .42);
System.out.println(1.00 – 9 * .10);

输出结果为:
0.6100000000000001
0.09999999999999998

你可能会认为,只要在打印之前将结果做一下舍入就可以解决这个问题,但遗憾的是,这种做法并不总是可行。
例如:假设你口袋里有1美元,你看到货架上有一排糖果,标价分别是10美分、20美分、30美分等等,一直到1美元。你打算从标价为10美分的开始买,每种买一颗,直到不能支付货架上的任何一种价格为止,那么你可以买多少颗糖果?还会找回多少零头?下面是一个简单的程序,用来解决这个问题:

double funds = 1.00;
int itemsBought = 0;
for (double price = .10; funds >= price; price += .10) {
funds -= price;
itemsBought++;
}
System.out.println(itemsBought + " items bought.");
System.out.println("Change: $" + funds);

如果运行这个程序你会发现你可以支付3颗糖果,并且还剩0.3999999999999999美元。显然这个答案是不正确的。解决这个问题的正确办法是使用BigDecimalintlong进行货币计算。

下面看看使用BigDecimal类型代替double的程序:

final BigDecimal TEN_CENTS = new BigDecimal(".10");
int itemsBought = 0;
BigDecimal funds = new BigDecimal("1.00");
for (BigDecimal price = TEN_CENTS; funds.compareTo(price) >= 0; price = price
.add(TEN_CENTS)) {
itemsBought++;
funds = funds.subtract(price);
}
System.out.println(itemsBought + " items bought.");
System.out.println("Money left over: $" + funds);


输出结果0.00和4 这样就正确了

关于BigDecimal的方法我们下去再仔细看看;
这里要说的是BigDecimal是引用数据类型

总而言之: 对于任何要精确答案的计算任务,请不要用double和float如果你想 计算十进制小数点,并且不介意非基本类型带来的不便,那就用BigDecimal;使用 BigDecimal还有个额外的好处就是他允许你完全控制舍入,每当一个操作涉及舍入的时候 它允许你从8种舍入模式中选择一种。如果你正确通过法定要求的舍入行为进行业务计算使用BigDecimal时非常方便的;如果性能十分关键,并且你又不介意自己记录十进制的 小数点,而且涉及数值不大,就可以使用int或者long; 如果数值范围没有超过9位的十进制数字,可以用int;如果数值范围没有超过18位的十进制数字,用long 如果范围超过了18位数,那我们必须使用BigDecimal。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  精确