org.javamoney.moneta.RoundedMoney-一些运算会产生取整值,而有些则不能

时间:2018-08-29 17:16:56

标签: java-money

如果我使用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 :(得分:0)

(刚刚找到了corresponding issue on GitHub

这可能是由于特定于实现的假设,即RoundedMoney实例将始终包含舍入值,但是,显然,该类的工厂方法和构造函数中未强制执行此值。您可以使用未取整的值来愉快地构建它。

在使用该类进行数学运算时,将应用一些算术优化:示例A1和C1在右侧使用加法和乘法的标识元素,因此它们实际上是无操作的,并且将返回this ,代表初始未取整值。示例A2和C2在理论上可以直接返回右侧运算符,但是缺少优化,因此RoundedMoney实际上开始计算(并四舍五入)结果。

示例B1只是翻转数字值的符号。这里,适用相同的假设:如果x是正确舍入的值,则-x也正确舍入了 [1] 。因此,RoundedMoney不会费心将舍入应用于新的数值。相比之下,示例B2并非经过优化,而是经过了计算和四舍五入。

因此,我认为真正的罪魁祸首是the factory methods,这不会将舍入应用于用户提供的值:

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进行四舍五入,因此可以很容易地传入一些任意的“非对称”运算符。