【问题标题】:How to handle precision and rounding errors in currency? [closed]如何处理货币的精度和舍入误差? [关闭]
【发布时间】:2016-08-31 18:32:48
【问题描述】:

我从其他 SO 问题中了解到的事情:

  • 我必须使用整数或 Java 的 BigDecimal(Java 是我的例子,尽管问题更广泛),而不是浮点类型。
  • 我必须至少比我想显示的小数位多存储一位。就我而言,我想显示 2 位小数。惯例是在数据库中使用小数(19,4)(在我的例子中是 Postgres,但这个问题更广泛)

当我在内存中有一个包含 101 个项目的账单,每个项目的成本为 1.00004999999999999 >

如果我在数据库中存储 4 位小数,每个项目将被四舍五入并存储为 1.0000 美元,当我从数据库中提取它们并将它们相加时,我得到 101.0000 美元,四舍五入为 101.00 美元。

如果我将存储精度提高到十进制 (19,5),我需要 1001 个项目而不是 101 个项目,但这仍然是可能的。它总是有可能发生,但一种可能的解决方案是大幅提高存储精度,使发生这种错误的可能性比闪电击中计算机的可能性小。

到目前为止,我的解决方案是永远不要使用 BigDecimal,而是始终使用 BigDecimal.setScale(4),它将其设置为 4 位小数。你会建议一种不同的方法吗?

我知道行不通的事情:

  • 计算存储后的总数。我需要在用户输入数据时使用 JavaScript 计算它。
  • 从不使用四舍五入的值进行计算。我不知道如何在不四舍五入的情况下存储它们
  • 从不向上取整,总是向下取整。如果我的税值为 1.005049999,它必须显示为 1.01。这是一个商业规则。我可以挑战它,但必须有一种不影响业务运作方式的方法。

其他注意事项:

  • 我不太关心执行速度或内存使用情况。

【问题讨论】:

  • 您是否打算让您的系统支持价格为1.00004999999999999 精度?通过指定如此精确的价格,您似乎在自相矛盾,但在数据库中使用DECIMAL(19,4)。是哪一个?
  • 这是一个数学事实,round(a+b+c) 通常不等于round(a)+round(b)+round(c)。您必须根据业务规则决定哪一个给出正确的值。
  • 我不编写会计软件,但我希望如果应用程序需要您进行舍入,它会要求您以特定应用程序的方式进行舍入。我>
  • 只是想知道 - 保留超过 2 位十进制数字的货币是否有意义?我的意思是你不能去银行要求支付 0.0099 美元,要么是 1 美分,要么什么都没有......
  • @Kayaman 你是对的,我的错。我不想支持1.0000499999999的个别价格。我对支持1.0000499999999 的项目总数没有兴趣,但除非我将所有内容汇总起来,否则它们在内存中的价值。我所需要的只是准确地显示 2 位小数。 DECIMAL(19,4) 来自我理解的一种普遍接受的存储货币的解决方案。我可以对此引用几个 SO 答案。并不意味着他们是对的。

标签: java currency


【解决方案1】:

始终使用整数数学计算货币。 切勿将货币存储为浮点值。 货币是固定点值。 将所有货币值存储为一个整数,该整数代表对您的系统重要的最小面额。 例如,如果您使用美元并且您关心的最小面额是一便士(0.01 美元),那么将货币值存储为便士计数。 如果您关心千分之一便士(0.00001 美元),请存储它。

【讨论】:

  • 谢谢,但我不明白这如何回答我的问题
  • @Blueriver 哦,但确实如此。 DwB 告诉您的是,您应该确定您计划处理的最小货币单位是什么,并将其作为计算的基础。例如,假设您要处理的最小货币单位是十分之一美分(即 0.001 美元)。然后,1000 的整数值将对应于 1 美分的 1000 个十分之一,或者正好是 1 美元。您必须完成一个额外的翻译步骤,但这样做可以缓解您所说的舍入问题。
【解决方案2】:

您可能需要考虑始终以全精度存储 BigDecimal 值,以全精度进行所有计算,并且仅在最后可能的时刻四舍五入该值。这样,您的舍入误差就不会累积。

但是,我必须质疑你的例子。您已经创建了正确答案为 $101.005 的情况。目前尚不清楚该值应如何四舍五入。传统上,我们将五舍五入,但这是因为,如果有任何其他数字,它更接近较高的值而不是较低的值。但是当它全为零时,它同样接近每个有效值,所以我不确定在这种情况下是否有正确的答案。

【讨论】:

  • 我的想法完全一样,但如果我想直到最后一刻,我会在内存中进行全精度计算,然后在将它们存储到数据库之前将它们四舍五入,我得到了我的问题在我的例子中显示。存储前的计算与存储后的计算不同。至于正确答案,我的例子的答案是 $101.005049999999999,它与每个有效值的接近程度并不相同,它更接近 101.01 而不是 101.00。你是对的,我应该在四舍五入后指定我的预期答案是 101.01
猜你喜欢
  • 1970-01-01
  • 2010-09-16
  • 1970-01-01
  • 1970-01-01
  • 2013-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多