我总是被告知永远不要用双类型或浮点类型来表示货币,这一次我向你提出一个问题:为什么?
我相信有一个很好的理由,我只是不知道是什么。
我总是被告知永远不要用双类型或浮点类型来表示货币,这一次我向你提出一个问题:为什么?
我相信有一个很好的理由,我只是不知道是什么。
当前回答
The result of floating point number is not exact, which makes them unsuitable for any financial calculation which requires exact result and not approximation. float and double are designed for engineering and scientific calculation and many times doesn’t produce exact result also result of floating point calculation may vary from JVM to JVM. Look at below example of BigDecimal and double primitive which is used to represent money value, its quite clear that floating point calculation may not be exact and one should use BigDecimal for financial calculations.
// floating point calculation
final double amount1 = 2.0;
final double amount2 = 1.1;
System.out.println("difference between 2.0 and 1.1 using double is: " + (amount1 - amount2));
// Use BigDecimal for financial calculation
final BigDecimal amount3 = new BigDecimal("2.0");
final BigDecimal amount4 = new BigDecimal("1.1");
System.out.println("difference between 2.0 and 1.1 using BigDecimal is: " + (amount3.subtract(amount4)));
输出:
difference between 2.0 and 1.1 using double is: 0.8999999999999999
difference between 2.0 and 1.1 using BigDecimal is: 0.9
其他回答
我对其中一些回答感到困扰。我认为双数和浮点数在财务计算中占有一席之地。当然,在使用整数类或BigDecimal类时,在加减非分数货币金额时,不会损失精度。但是,当执行更复杂的操作时,无论您如何存储这些数字,您经常会得到小数点后几位或许多位的结果。问题在于你如何呈现结果。
如果你的结果是在四舍五入和四舍五入之间的边缘,最后一分真的很重要,你可能应该告诉观众答案几乎在中间——通过显示更多的小数点后数位。
双精度浮点数的问题是,当它们被用来组合大数和小数时。在java中,
System.out.println(1000000.0f + 1.2f - 1000000.0f);
结果
1.1875
The result of floating point number is not exact, which makes them unsuitable for any financial calculation which requires exact result and not approximation. float and double are designed for engineering and scientific calculation and many times doesn’t produce exact result also result of floating point calculation may vary from JVM to JVM. Look at below example of BigDecimal and double primitive which is used to represent money value, its quite clear that floating point calculation may not be exact and one should use BigDecimal for financial calculations.
// floating point calculation
final double amount1 = 2.0;
final double amount2 = 1.1;
System.out.println("difference between 2.0 and 1.1 using double is: " + (amount1 - amount2));
// Use BigDecimal for financial calculation
final BigDecimal amount3 = new BigDecimal("2.0");
final BigDecimal amount4 = new BigDecimal("1.1");
System.out.println("difference between 2.0 and 1.1 using BigDecimal is: " + (amount3.subtract(amount4)));
输出:
difference between 2.0 and 1.1 using double is: 0.8999999999999999
difference between 2.0 and 1.1 using BigDecimal is: 0.9
浮点数和双精度数是近似的。如果你创建了一个BigDecimal并将一个float传递给构造函数,你会看到float实际等于什么:
groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375
这可能不是您想要的表示1.01美元的方式。
问题是IEEE规范没有一种方法来精确地表示所有的分数,其中一些分数最终是重复的分数,所以你最终会得到近似错误。由于会计人员喜欢精确到每一分钱,如果客户支付账单,在付款处理后他们欠0.01,他们会被收取费用或无法关闭他们的帐户,那么最好使用精确的类型,如decimal(在c#中)或Java. math. bigdecimal。
这并不是说如果你四舍五入,误差就无法控制:请参阅Peter Lawrey的这篇文章。只是从一开始就不用四舍五入更容易。大多数处理资金的应用程序不需要大量的数学运算,操作包括添加东西或将金额分配到不同的存储空间。引入浮点数和舍入只会使事情复杂化。
Float is binary form of Decimal with different design; they are two different things. There are little errors between two types when converted to each other. Also, float is designed to represent infinite large number of values for scientific. That means it is designed to lost precision to extreme small and extreme large number with that fixed number of bytes. Decimal can't represent infinite number of values, it bounds to just that number of decimal digits. So Float and Decimal are for different purpose.
有一些方法可以管理货币值的错误:
使用长整数,以分计算。 使用双精度,保持你的有效数字为15,这样小数可以精确模拟。在显示值之前舍入;做计算时经常四舍五入。 使用像Java BigDecimal这样的十进制库,这样就不需要使用double来模拟十进制。
附注:有趣的是,大多数品牌的手持科学计算器工作在十进制而不是浮点数。所以没有人抱怨浮点数转换错误。
这个问题的许多答案都讨论了IEEE和围绕浮点算法的标准。
我的背景不是计算机科学(物理和工程),我倾向于从不同的角度看问题。对我来说,我在数学计算中不使用double或float的原因是我会丢失太多的信息。
有什么替代方案?有很多(还有很多我不知道的!)
Java中的BigDecimal原产于Java语言。 Apfloat是另一个用于Java的任意精度库。
c#中的十进制数据类型是微软的. net中28位有效数字的替代方案。
SciPy (Scientific Python)可能还可以处理财务计算(我还没有尝试过,但我怀疑是这样)。
GNU多精度库(GMP)和GNU MFPR库是C和c++的两个免费的开源资源。
还有用于JavaScript(!)和PHP的精确数值库,我认为它们可以处理财务计算。
对于许多计算机语言,也有专有的(特别是Fortran)和开源的解决方案。
我不是训练出来的计算机科学家。然而,我倾向于在Java中使用BigDecimal,在c#中使用decimal。我还没有尝试过我列出的其他解决方案,但它们可能也非常好。
对我来说,我喜欢BigDecimal是因为它支持的方法。c#的十进制非常好,但我还没有机会尽可能多地使用它。我在业余时间做我感兴趣的科学计算,BigDecimal似乎工作得很好,因为我可以设置浮点数的精度。BigDecimal的缺点是什么?它有时会很慢,特别是当你使用除法的时候。
为了提高速度,您可以查看C、c++和Fortran中的免费和专有库。