简体   繁体   English

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

[英]org.javamoney.moneta.RoundedMoney - some operations result in a rounded value some are not

If i use org.javamoney.moneta and run the following program, i got some contrary results for some operations on org.javamoney.moneta.RoundedMoney . 如果我使用org.javamoney.moneta并运行以下程序,则对org.javamoney.moneta.RoundedMoney进行的某些操作会得到相反的结果。 Sometimes the resulted value is rounded some times it is not. 有时,结果值会四舍五入而不是四舍五入。

Am I using the class wrong or is that a bug? 我使用的课程是否错误或是一个错误?

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());
    }
}

Output: 输出:

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

The used maven dependency is: 使用的maven依赖项是:

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

(Just found the corresponding issue on GitHub 😛) (刚刚在GitHub found 找到了相应的问题

This is probably due to the implementation-specific assumption that RoundedMoney instances will always contain rounded values, but, apparently, this is not enforced in the factory methods and constructors of that class. 这可能是由于特定于实现的假设所决定的,即RoundedMoney实例将始终包含舍入的值,但是,显然,这不是在该类的工厂方法和构造函数中强制执行的。 You can happily construct it with unrounded values. 您可以使用未取整的值来愉快地构造它。

When doing math with that class, some arithmetic optimizations apply: Examples A1 and C1 use the identity elements of addition and multiplication on the right hand side, hence they are effectively no-ops, and this will be returned, representing the initial unrounded value. 当与类做数学,一些算术优化应用:实施例A1和C1使用加法和乘法在右手侧的身份元素,因此,它们是有效的空操作,并且this将被返回,表示初始未舍入值。 Examples A2 and C2 could theoretically return the right hand side operator directly, but that optimization is missing, so RoundedMoney actually starts to calculate (and round) the result. 示例A2和C2在理论上可以直接返回右侧运算符,但是缺少优化,因此RoundedMoney实际上开始计算(并四舍五入)结果。

Example B1 just flips the sign of the number value. 示例B1只是翻转数字值的符号。 Here, the same assumptions apply: If x is a correctly rounded value, then -x is also correctly rounded [1] . 在这里,适用相同的假设:如果x是正确舍入的值,则-x也正确舍入[1] So, RoundedMoney won't bother to apply the rounding to the new numeric value. 因此, RoundedMoney不会费心将舍入应用于新的数值。 In contrast, example B2 isn't optimized, but calculated and rounded. 相反,示例B2并未进行优化,而是经过了计算和四舍五入。

So, I think the actual culprits are the factory methods , which won't apply the rounding to user-supplied values: 因此,我认为真正的罪魁祸首是工厂方法 ,该方法不会将舍入应用于用户提供的值:

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);
}

should most probably be 应该是

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);
}

I'd argue this is a bug, but since there's not much (no?) documentation about how that class is supposed to be used – or not used, for that matter – it might even be intended behavior? 我认为这是一个错误,但是由于没有太多(否?)有关该类应如何使用(或不使用)的文档,它甚至可能是预期的行为?


[1]: This is interesting. [1]:这很有趣。 Is there any rounding which won't satisfy that assumption? 是否有任何舍入不能满足该假设? In fact, since RoundedMoney leverages a MoentaryOperator to apply the rounding, one could easily pass in some arbitrary "non-symmetric" operator. 实际上,由于RoundedMoney利用MoentaryOperator进行了四舍五入,因此可以轻松传入一些任意的“非对称”运算符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM