java-money - 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>
解决方案
这可能是由于特定于实现的假设,即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
利用 aMoentaryOperator
来应用舍入,因此可以很容易地传入一些任意的“非对称”运算符。
推荐阅读
- heroku - 关闭控制台时,Discord bot 关闭
- r - 如何聚合具有分类响应的数据以获取 R 中每种响应类型的百分比?
- javascript - 将原始二进制数据写入 Buffer
- reactjs - React-Redux:为什么我们需要用 Provider 包装组件?
- vue.js - 你可以使用 Gitlab CI/CD 构建一个 Vue 页面并使用 Gitlab 页面托管它吗?
- javascript - MongoDB - 不同响应大小的并发响应速度慢且速度差异巨大
- php - 我在我的 PHP 表单中添加了新的输入字段,但它不再将数据发布到 SQL 服务器
- algorithm - 有多少种方法可以给圆形中未着色的边缘着色
- arrays - ReactJS ES6 数组过滤
- jquery - 如何在 django 中自动计算字段?