4

尝试Money从 MySQL 数据库读取的 BigDecimal 创建 Joda-Money 对象会引发错误。

这段代码:

PreparedStatement p_stmt = ...;
ResultSet results = ...;
Money amount = Money.of(CurrencyUnit.USD, results.getBigDecimal("amount"));

抛出这个:

java.lang.ArithmeticException: Scale of amount 1.0000 is greater than the scale of the currency USD
    at org.joda.money.Money.of(Money.java:74)
    at core.DB.getMoney(DB.java:4821)

我实际上已经通过添加四舍五入解决了这个错误:

Money amount = Money.of(CurrencyUnit.USD, results.getBigDecimal("amount"), RoundingMode.HALF_UP);

但是,我对这种解决方案持怀疑态度。是最好的吗?

我正在DECIMAL(19,4)按照这个 SO answer将货币价值存储在数据库中。老实说,这让我有点困惑,为什么那里的回答者要求在 DB 上精确到小数点后 4 位,但我倾向于相信高价值的答案,我认为他们知道他们在说什么,我会后悔没有关注他们的建议。然而,Joda-Money 不喜欢美国货币的小数点后 4 位精度。也许DECIMAL(19,4)是他们需要小数点后 4 位精度的国际标准?没有把握..

为了使问题简洁:

  1. RoundingMode.HALF_UP解决此错误的理想解决方案吗?
  2. 我应该将 MySQL DB 的精度从 更改DECIMAL(19,4)DECIMAL(19,2)吗?
  3. 有没有办法改变 Joda-Money 的精度?如果是:我应该吗?
4

3 回答 3

2

Joda Money 和 BigDecimal 为每种货币类型定义了一个“比例”。

规模是没有。货币使用的小数位数。

所以 USD 有两个小数位(小数点后两位小数)。

如果您尝试从具有两个以上小数位的源实例化美元货币的 Money 实例,那么您将收到缩放错误。例如

20.99 is fine
20.991 throws an error.

其他货币允许不同的小数位数。

根据文档RoundMode.UNNECESSARY,如果实际需要舍入,实际上会抛出异常。

例如:假设美元的小数位数为 2,则需要小数点后 2 位。

使用:

Money money = Money.of(CurrencyUnit.USD, value, RoundingMode.UNNECESSARY);

价值:

1.20 - works fine
1.200 - works fine as whilst extra digit rounding isn't necessary.
1.201 - throws an exception as rounding is necessary

四舍五入和金钱总是一个问题。我的方法是在数据库中存储正确的比例

例如:如果 USD 则 DECIMAL(19,2)

使用RoundingMode.HALF_UP.

做乘法和除法时要小心。在除法之前进行乘法将导致更少的舍入问题。如果你在做算术的时候四舍五入,你应该没问题。

于 2018-08-01T00:21:05.687 回答
1

是的,不是一个好的解决方案。您代表的是金钱,因此您不能向上/向下舍入并损失几美分:)。

问题是 CurrencyUnit.USD 的位数为 2,例如:1.45,而来自数据库的数字可能类似于 1.45343,从而导致规模差异。尝试用 BigMoney 而不是 Money 来代表钱。

BigMoney amount = BigMoney .of(CurrencyUnit.USD, results.getBigDecimal("amount"));
于 2014-05-30T17:17:12.087 回答
0

有同样的问题并通过添加舍入模式来解决它不必要的:

    @Test(dataProvider = "moneyValueProvider")
    public void testMoneyFromFloat(String currency, Float value) {
        CurrencyUnit cu = CurrencyUnit.of(currency);
        //Double dVal = Double.valueOf(value.toString());
        BigDecimal bigDecimal = new BigDecimal(value.toString());
        Money money = Money.of(cu, bigDecimal, RoundingMode.UNNECESSARY);
        Assert.assertNotNull(money);
        Assert.assertEquals(bigDecimal.doubleValue(), money.getAmount().doubleValue());
    }

    @DataProvider
    public static Object[][] moneyValueProvider() {
        return new Object[][] {
                    {"GBP", 22.5f},
                    {"GBP", 57.8f},
                    {"JPY", 15000.0f}

    };
于 2016-10-25T20:43:27.090 回答