本问题已经有最佳答案,请猛点这里访问。
Possible Duplicate:
Why not use Double or Float to represent currency?
我正在为我的高中课程编写一个Java的基本命令行程序。我们现在只处理变量。它用于计算购买后您的零钱中任何类型的纸币和硬币的数量。这是我的计划:
class Assign2c {
public static void main(String[] args) {
double cost = 10.990;
int paid = 20;
double change = paid - cost;
int five, toonie, loonies, quarter, dime, nickel, penny;
five = (int)(change / 5.0);
change -= five * 5.0;
toonie = (int)(change / 2.0);
change -= toonie * 2.0;
loonies = (int)change;
change -= loonies;
quarter = (int)(change / 0.25);
change -= quarter * 0.25;
dime = (int)(change / 0.1);
change -= dime * 0.1;
nickel = (int)(change / 0.05);
change -= nickel * 0.05;
penny = (int)(change * 100);
change -= penny * 0.01;
System.out.println("$5 :" + five);
System.out.println("$2 :" + toonie);
System.out.println("$1 :" + loonies);
System.out.println("$0.25:" + quarter);
System.out.println("$0.10:" + dime);
System.out.println("$0.05:" + nickel);
System.out.println("$0.01:" + penny);
}
}
这一切都应该有效,但在最后一步,当剩下0.01美元时,硬币的数量应该是1,而不是0。在进入代码并将更改值输出到控制台几分钟后,我发现在最后一步,当Change=0.01时,它变为0.0099999999787。为什么会这样?
不能用二进制正确表示所有的小数部分。使用整数执行这些操作,或者自己处理舍入。这个问题(或它的变体)在这里被问了数百次。这里有一个很好的参考:download.oracle.com/docs/cd/e19957-01/806-3568/…
只做mod:p比较容易。
aa然后我们又去了……
即使我从未使用bigdecimal(因为我发现msexcell有不同,但这只是我的问题),请阅读[来自otn的一个线程][1],[1]:forums.oracle.com/forums/…
用double表示货币是个坏主意,为什么不用double或float表示货币呢?.我建议使用BigDecimal或以美分计算。
用美分计算几乎总是比使用BigDecimalimo更好。
@马特球,有明显的不动点算术问题,即你要么失去准确性,不是整数,或需要另一个解决方案。因为中间计算的精确度只有1美分,这听起来像是灾难的前兆。
@VOO:对于用户正在做的应用程序来说,1分的准确度是非常好的。
@奥利·查尔斯沃思你是说一个没有实际用途的应用程序?;)当然可以,但了解可能解决方案的所有优点和缺点总是一个好主意(尤其是如果你这样做的唯一原因是学习新的东西)。如果准确度很重要的话,用美分计算当然不是"几乎总是比大小数更可取"的(而且根据你所做的计算,最终结果可能明显会相差1%以上)。我能看到的大小数的唯一缺点是性能
0.01在浮点值中没有精确的表示(对于这一点,0.1和0.2都没有)。
你可能应该用整数类型来做你所有的数学运算,代表硬币的数量。
真的。有种感觉,但我不知道。那我用美分来计算这个
+1好主意,但不总是适用于"bigdecimal"是不可能分解的方式。
doubles内部不是十进制的,而是二进制的。它们的存储格式相当于"100101乘以10000"(我正在简化,但这是基本思想)。不幸的是,这些二进制值的组合并不能精确到小数0.01,这就是其他答案所说的浮点数字不100%准确,或者0.01在浮点中没有精确的表示。
处理这个问题的方法有很多种,有些比其他更复杂。在您的情况下,最好的解决方案可能是在任何地方都使用int,并以美分为单位保留值。
正如其他人已经说过的,不要在财务计算中使用双数。
本文http://download.oracle.com/docs/cd/e19957-01/806-3568/ncg_goldberg.html(每个计算机科学家都应该知道浮点运算的内容)是理解计算机浮点运算的必读材料。
浮点数永远不会100%准确(不完全正确,请参阅下面的注释)。你不应该直接比较它们。也是整数舍入。做到这一点的最好方法可能是用美分计算,然后再转换成美元(1美元=100美分)。通过转换为整数,您将失去精度。
说"浮点数永远不会100%准确"是不对的。
我想我应该说你不能可靠地比较它们?
0.5是100%准确的。正如现在(或者可以是)2的大多数幂,以及2的大多数倍数(只要尾数可以容纳分数的分子)。当你得到分母不是2的幂的分数,或者分子太大不能完全表示的分数时,你就会遇到舍入误差。
啊,是的,你是对的。谢谢。
我相信你知道有些分数的十进制表示法终止了(例如0.01),而有些则没有终止(例如2/3=0.66666…)。问题是,分数的终止取决于你所处的基数;特别是,.01不以二进制结尾,所以即使double提供了很多精度,它也不能精确地表示.01。正如其他人所说,使用bigdecimal或定点整数计算(将所有内容转换为美分)可能是最适合使用货币的方法;要了解有关浮点的更多信息,可以从浮点指南开始——每个程序员都应该了解浮点算法。
这是一个多次出现的问题。底线是,在使用二进制浮点的计算机(Java需要的)上,只有分母是2的幂的分数可以精确地表示。
同样的问题以十进制出现。例如,1/3变成0.3333333…,因为3不是10的因子(我们用十进制表示的基数)。同样,1/17、1/19等。
在二进制浮点中,同样的基本问题出现了。主要的区别是,在十进制中,由于5是10的一个因子,1/5可以精确表示(1/5的倍数也是如此)。由于5不是2的因子,1/5不能用二进制浮点精确表示。
然而,与人们普遍认为的相反,有些分数可以精确表示——特别是那些分母只有2作为素数的分数(例如,1/8或1/256可以精确表示)。
它是一个浮动(双)
你不应该用它来计算钱……
我建议使用int值并使用penny