org.javamoney.moneta.RoundedMoney - некоторые операции приводят к округленному значению, некоторые - нет - PullRequest
0 голосов
/ 29 августа 2018

Если я использую org.javamoney.moneta и запускаю следующую программу, я получаю некоторые противоположные результаты для некоторых операций на org.javamoney.moneta.RoundedMoney. Иногда приведенное значение округляется, иногда это не так.

Я использую класс неправильно или это ошибка?

import java.math.BigDecimal;
import javax.money.CurrencyUnit;
import javax.money.Monetary;
import org.javamoney.moneta.RoundedMoney;

public final class RoundedMoneyRounding
{
    private RoundedMoneyRounding()
    {
    }


    public static void main(final String... args)
    {
        final CurrencyUnit usd = Monetary.getCurrency("USD");
        final RoundedMoney halfcent = RoundedMoney.of(new BigDecimal("0.005"), usd);
        final RoundedMoney zero = RoundedMoney.of(BigDecimal.ZERO, usd);

        System.out.append("A1. 0.005 + 0 = ").println(//
                                                      halfcent.add(zero) //
                                                                      .getNumber().numberValue(BigDecimal.class).toPlainString());

        System.out.append("A2. 0 + 0.005 = ").println(//
                                                      zero.add(halfcent) //
                                                                      .getNumber().numberValue(BigDecimal.class).toPlainString());

        System.out.println("----");

        System.out.append("B1: -0.005 = ").println(//
                                                   halfcent.negate() //
                                                                   .getNumber().numberValue(BigDecimal.class).toPlainString());

        System.out.append("B2: 0.005 * -1 = ").println(//
                                                       halfcent.multiply(new BigDecimal("-1")) //
                                                                       .getNumber().numberValue(BigDecimal.class).toPlainString());

        System.out.println("----");

        System.out.append("C1: 0.005 * 1 = ").println(//
                                                      halfcent.multiply(BigDecimal.ONE) //
                                                                      .getNumber().numberValue(BigDecimal.class).toPlainString());

        System.out.append("C2: 0.005 * 1.1 = ").println(//
                                                        halfcent.multiply(new BigDecimal("1.1")) //
                                                                        .getNumber().numberValue(BigDecimal.class).toPlainString());

        System.out.println("----");

        System.out.append("D1: 0.005 * 2 = ").println(//
                                                      halfcent.multiply(new BigDecimal("2")) //
                                                                      .getNumber().numberValue(BigDecimal.class).toPlainString());

        System.out.append("D2: (0.005 * 2) / 2 = ").println(//
                                                            halfcent.multiply(new BigDecimal("2")).divide(new BigDecimal("2")) //
                                                                            .getNumber().numberValue(BigDecimal.class).toPlainString());
    }
}

Выход:

A1. 0.005 + 0 = 0.005
A2. 0 + 0.005 = 0
----
B1: -0.005 = -0.005
B2: 0.005 * -1 = 0
----
C1: 0.005 * 1 = 0.005
C2: 0.005 * 1.1 = 0.01
----
D1: 0.005 * 2 = 0.01
D2: (0.005 * 2) / 2 = 0

Используемая maven зависимость:

<dependency>
    <groupId>org.javamoney</groupId>
    <artifactId>moneta</artifactId>
    <version>1.3</version>
    <type>pom</type>
</dependency>

1 Ответ

0 голосов
/ 26 февраля 2019

(только что нашел соответствующую проблему на GitHub ?)

Вероятно, это связано с предположением, связанным с реализацией, что экземпляры RoundedMoney всегда будут содержать округленные значения, но, очевидно, это не применяется в фабричных методах и конструкторах этого класса. Вы можете счастливо построить его с необоснованными ценностями.

При выполнении математических операций с этим классом применяется некоторая арифметическая оптимизация: в примерах A1 и C1 используются элементы идентичности сложения и умножения в правой части, следовательно, они фактически являются no-ops, и будет возвращено this, представляющее начальное необоснованное значение. Примеры A2 и C2 теоретически могут напрямую возвращать правый оператор, но эта оптимизация отсутствует, поэтому RoundedMoney фактически начинает вычислять (и округлять) результат.

Пример B1 просто переворачивает знак числового значения. Здесь применяются те же допущения: если x является правильно округленным значением, то -x также правильно округляется [1] . Таким образом, RoundedMoney не потрудится применить округление к новому числовому значению. Напротив, пример B2 не оптимизирован, а рассчитан и округлен.

Итак, я думаю, что фактические виновники фабричные методы , которые не будут применять округление к предоставленным пользователем значениям:

public static RoundedMoney of(BigDecimal number, CurrencyUnit currency) {
    return new RoundedMoney(number, currency, Monetary.getDefaultRounding());
}

public static RoundedMoney of(BigDecimal number, CurrencyUnit currency, MonetaryOperator rounding) {
    return new RoundedMoney(number, currency, rounding);
}

скорее всего должно быть

public static RoundedMoney of(BigDecimal number, CurrencyUnit currency) {
    return of(number, currency, Monetary.getDefaultRounding());
}

public static RoundedMoney of(BigDecimal number, CurrencyUnit currency, MonetaryOperator rounding) {
    return new RoundedMoney(number, currency, rounding).with(rounding);
}

Я бы поспорил, что это ошибка, но так как нет большой (нет?) Документации о том, как этот класс предполагается использовать - или вообще не использовать - это может быть даже предполагаемое поведение?


[1]: Это интересно. Есть ли округления, которые не удовлетворят этому предположению? Фактически, поскольку RoundedMoney использует MoentaryOperator для применения округления, можно легко передать некоторый произвольный "несимметричный" оператор.

...