我总是被告知永远不要用双类型或浮点类型来表示货币,这一次我向你提出一个问题:为什么?

我相信有一个很好的理由,我只是不知道是什么。


当前回答

我对其中一些回答感到困扰。我认为双数和浮点数在财务计算中占有一席之地。当然,在使用整数类或BigDecimal类时,在加减非分数货币金额时,不会损失精度。但是,当执行更复杂的操作时,无论您如何存储这些数字,您经常会得到小数点后几位或许多位的结果。问题在于你如何呈现结果。

如果你的结果是在四舍五入和四舍五入之间的边缘,最后一分真的很重要,你可能应该告诉观众答案几乎在中间——通过显示更多的小数点后数位。

双精度浮点数的问题是,当它们被用来组合大数和小数时。在java中,

System.out.println(1000000.0f + 1.2f - 1000000.0f);

结果

1.1875

其他回答

我对其中一些回答感到困扰。我认为双数和浮点数在财务计算中占有一席之地。当然,在使用整数类或BigDecimal类时,在加减非分数货币金额时,不会损失精度。但是,当执行更复杂的操作时,无论您如何存储这些数字,您经常会得到小数点后几位或许多位的结果。问题在于你如何呈现结果。

如果你的结果是在四舍五入和四舍五入之间的边缘,最后一分真的很重要,你可能应该告诉观众答案几乎在中间——通过显示更多的小数点后数位。

双精度浮点数的问题是,当它们被用来组合大数和小数时。在java中,

System.out.println(1000000.0f + 1.2f - 1000000.0f);

结果

1.1875

如果你的计算涉及到不同的步骤,任意的精度算法都不能100%覆盖你。

使用完美的结果表示(使用自定义Fraction数据类型,将除法操作批处理到最后一步)并且仅在最后一步转换为十进制的唯一可靠方法。

任意精度不会有帮助,因为总有可能有很多小数点后的数字,或者一些结果,如0.6666666……最后一个例子没有任意的表示法。所以每一步都会有小误差。

这些错误会累积起来,最终可能变得不再容易被忽视。这被称为错误传播。

美国货币可以很容易地用美元和美分来表示。整数是100%精确的,而浮点二进制数并不完全匹配浮点小数。

这个问题的许多答案都讨论了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中的免费和专有库。

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来模拟十进制。

附注:有趣的是,大多数品牌的手持科学计算器工作在十进制而不是浮点数。所以没有人抱怨浮点数转换错误。