您有什么建议?
美元金额应该使用float还是decimal数据类型?
两者的优缺点是什么?
在我们的每日Scrum中提到的一个缺点是当您计算超过两个小数位的金额时必须小心。听起来你必须将金额舍入到两个小数位。
另一个缺点是所有显示和打印金额都必须有一个格式语句,显示两个小数位。我注意到有几次没有这样做,金额看起来不正确。(例如10.2或10.2546)
一个优点是只使用浮点数需要8个字节的磁盘空间,而十进制数需要9个字节(十进制12,2)。
应该使用Decimal数据类型而不是Float来表示美元金额。
答案很简单:永远不要使用Float,绝对不能!
根据IEEE 754标准,Float总是二进制的,只有新标准IEEE 754R定义了十进制格式。许多小数的二进制部分永远无法等于精确的十进制表示。
任何二进制数可以写成m/2^n
(其中m
、n
为正整数),任何十进制数可以写成m/(2^n*5^n)
。
由于二进制缺乏质因子5
,所有二进制数都可以被十进制精确表示,但反之则不然。
0.3 = 3/(2^1 * 5^1) = 0.3
0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]
1/4 1/8 1/16 1/32
因此,你最终得到的数字要么比给定的十进制数更高,要么更低。总是如此。
为什么这很重要?四舍五入。
正常四舍五入意味着0..4向下取整,5..9向上取整。所以如果结果是 0.049999999999
... 或者 0.0500000000
... 这在计算机中很重要。你可能知道它代表了5分钱,但计算机不知道,会将 0.4999
... 向下取整(错误的),将 0.5000
... 向上取整(正确的)。
考虑到浮点运算的结果总是包含误差项,这个决定纯粹靠运气。如果你想用二进制数进行小数四舍五入处理,那就变得更加困难了。
不信?你坚持认为你的账户系统完全没有问题吗?资产和负债相等吗?那就拿出每个条目的给定格式化数字,解析它们,并使用独立的十进制系统对它们求和!
将其与格式化后的总和进行比较。哎呀,有什么问题,不是吗?
对于这种计算,需要极高的精度和准确性(我们使用了Oracle的FLOAT),以便记录“每个亿分之一便士”的累积。
这并不能解决这个错误。因为所有人都会自动假设计算机正确求和,实际上几乎没有人独立检查。
首先,你应该阅读计算机科学家应该了解的浮点运算知识。然后,你应该真正考虑使用一些固定位数/任意精度数字包(例如Java BigNum或Python decimal模块)。否则,你将面临一个痛苦的世界。然后确定是否使用本地SQL十进制类型已经足够。
浮点数和双精度数存在(过去存在)是为了利用现在基本过时的x87浮点协处理器。如果你关心计算的精度和/或不能完全弥补它们的限制,请不要使用它们。
问问你的会计师!如果你使用浮点数,他们会对你皱眉头。像David Singer所说,只有在你不关心精度时才使用浮点数。尽管涉及到金钱时,我总是反对使用浮点数。
在会计软件中,不能接受浮点数。请使用四位小数的十进制数。
这里有一些背景信息...
没有任何数字系统可以准确处理所有的实数。它们都有其局限性,包括标准IEEE浮点和带符号十进制。IEEE浮点每使用一位更加精确,但在这里并不重要。
金融数字基于数个世纪的纸笔实践,并伴随着相关惯例。它们相当准确,但更重要的是,它们是可复制的。两个会计师使用各种数字和利率应该得出相同的数字。任何差异的空间都是欺诈的空间。
因此,在金融计算中,正确答案是与擅长算术的注册会计师得出的相同的答案。这是十进制算术,而不是IEEE浮点。
对于计算,你会遇到更多的问题 - 不准确度只是一个微小的部分,但将其放入幂函数中,它很快就变得显著。
然而,小数并不非常适用于任何类型的数学运算 - 例如,没有原生支持小数幂。
我建议使用64位整数来存储金额,以分为单位。
浮点数不是精确的表示方式,可能存在精度问题,例如在添加非常大和非常小的值时。这就是为什么建议使用十进制类型来处理货币,即使精度问题可能很少出现。
为了澄清,十进制12,2类型将完全存储这些14个数字,而浮点数不会,因为它在内部使用二进制表示。例如,0.01无法被浮点数精确表示 - 最接近的表示实际上是0.0099999998