org.javamoney.moneta.RoundedMoney - некоторые операции приводят к округленному значению, некоторые - нет
Если я использую 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>
0 ответов
(Только что нашел соответствующую проблему на GitHub)
Это, вероятно, связано с предположением, что RoundedMoney
экземпляры всегда будут содержать округленные значения, но, по-видимому, это не применяется в фабричных методах и конструкторах этого класса. Вы можете счастливо построить это с необоснованными ценностями.
При выполнении математических операций с этим классом применяется некоторая арифметическая оптимизация: в примерах A1 и C1 используются элементы тождества сложения и умножения в правой части, следовательно, они практически не используются, и 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
чтобы применить округление, можно легко передать какой-нибудь произвольный "несимметричный" оператор.