如何处理货币的精度和舍入误差?

Blu*_*ver 1 java currency

我从其他 SO 问题中知道的事情:

  • 我必须使用整数或 Java 的 BigDecimal(Java 是我的情况,但问题更广泛),而不是浮点类型。
  • 我必须至少存储比我想显示的小数位多一位。就我而言,我想显示 2 个小数位。约定是在数据库中使用 decimal(19,4) (在我的情况下它是 Postgres,但同样这个问题更广泛)

当我在内存中有 101 件物品的账单,每件物品价值 1.00004999999999999 时,我将所有价值为 1.000049999999999999 美元的 BigDecimals(或所有整数,如果我选择该表示法)相加,计算出的总数为 101.00509.9999 美元,即 1999999999 美元。

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

如果我将存储精度提高到十进制(19,5),我需要 1001 个项目而不是 101 个项目才能实现,但这仍然是可能的。它总是可能发生的事情,尽管一种可能的解决方案是将存储精度提高到如此之多,以至于发生这种错误的可能性比闪电击中计算机的可能性要小。

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

我知道行不通的事情:

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

其他注意事项:

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

DwB*_*DwB 6

始终对货币使用整数数学。切勿将货币存储为浮点值。货币是一个定点值。将所有货币值存储为一个整数,表示对您的系统很重要的最小面额。例如,如果您使用美元并且您关心的最小面额是一分钱 ($0.01),那么将货币值存储为一分钱的数量。如果您关心一分钱 (0.00001 美元) 的千分之一,则将其存储。

  • @Blueriver 哦,但确实如此。DwB 告诉您的是,您应该确定您计划处理的最小货币单位是什么,并将其作为计算的基础。例如,假设您要处理的最小货币单位是十分之一美分(即 0.001 美元)。那么,'1000' 的整数值将对应于一分的十分之 1000,即 1 美元。您必须执行一个额外的翻译步骤,但这样做可以减轻您所说的舍入问题。 (2认同)