JavaMoney / jsr354-ri

JSR 354 - Moneta: Reference Implementation
Other
334 stars 100 forks source link

Money (USD) calculations not rounding properly #387

Open arnaldop opened 1 year ago

arnaldop commented 1 year ago

The method below returns true for "USD 0.00".

@Override
public boolean warn() {
    return hasDisplayOptions(ExportDisplayOptions.ALWAYS_WARN)
           || value == null
           || value.isNegative()
            //               || is(new BigDecimal(value.getNumber().doubleValue())
            //                             .setScale(2, RoundingMode.HALF_EVEN))
            //                       .isNegative()
            ;
}

All monetary amounts enter my application through code like this:

public static final CurrencyUnit DOLLAR = Monetary.getCurrency(Locale.US);
public static MonetaryAmount of(@NonNull final String value) {
    return Money.of(new BigDecimal(value), DOLLAR);
}
public static MonetaryAmount of(final double value) {
    return Money.of(value, DOLLAR);
}
public static MonetaryAmount of(@NonNull final BigDecimal value) {
    return Money.of(value, DOLLAR);
}

As the screenshot below shows, after a number of calculations, my Money object that should only have 2 decimal places still holds 4, and it considers the full number when evaluating isNegative().

image

My understanding was that by using Moneta with USD, I would be guaranteed that monetary amounts would round up to the 2nd decimal place to match the US Dollar. Am I misusing the library or is this a bug?

Note: lots of adding, subtracting, and applying percentages is done to these numbers. I can try to come up with a small example, but I figured I'd ask here if this was a known issue. I saw #384, #304 and #277, but I'm using Money, not FastMoney nor RoundedMoney.

Using version 1.4.2.

kewne commented 4 months ago

As far as I can tell from looking at the spec, there is nothing that guarantees or even suggests that monetary amounts are implicitly rounded while performing calculations.

To me, this makes sense because it would otherwise make it impossible to sum amounts that round to zero.

If implicit rounding after each operation is required, RoundedMoney should be used. Otherwise, it is up to client code to perform the rounding explicitly.

In this case, I agree it is misleading to have toString perform rounding, thus misrepresenting the underlying amount. I think it would be an improvement to change it to something like number.toString + ' ' + currency.getCurrencyCode that would make the issue obvious but, at this point, it would be a breaking change despite not being in the spec.

In other words, the current behavior with regards to rounding seems to be correct.