我的Java程序以高精度计算为中心,需要精确到至少120个小数位 因此,程序中的所有非整数都将由BigDecimals表示。
显然我需要指定BigDecimals舍入的准确性,以避免无限小数表达式等。
目前,我发现必须在BigDecimal的每个实例化或数学运算中指定准确性是一件非常麻烦的事。
有没有办法设定全球准确度'对于所有BigDecimal计算?
(例如python中 Decimal 模块的Context.prec()
)
由于
规格:
Java jre7 SE
Windows 7(32)
答案 0 :(得分:11)
(差不多)原创
不是那么简单,但您可以创建MathContext
并将其传递给所有BigDecimal
构造函数以及执行操作的方法。
<强>修强>
或者,您可以通过提供正确的BigDecimal
并使用MathContext
的舍入版本来扩展divide
并覆盖您要使用的所有操作:
public class MyBigDecimal extends BigDecimal {
private static MathContext context = new MathContext(120, RoundingMode.HALF_UP);
public MyBigDecimal(String s) {
super(s, context);
}
public MyBigDecimal(BigDecimal bd) {
this(bd.toString()); // (Calls other constructor)
}
...
public MyBigDecimal divide( BigDecimal divisor ){
return new MyBigDecimal( super.divide( divisor, context ) );
}
public MyBigDecimal add( BigDecimal augend ){
return new MyBigDecimal( super.add( augend ) );
}
...
}
答案 1 :(得分:4)
创建一个BigDecimalFactory
类,其中静态工厂方法匹配接受MathContext
的所有构造函数 - 除了MathContext
实例在工厂内并在启动时静态初始化。这是一个片段:
public class BigDecimalFactory {
public static BigDecimal newInstance (BigInteger unscaledVal, int scale) {
return new BigDecimal (unscaledVal, scale, _mathContext);
}
// . . . other factory methods for other BigDecimal constructors
private static final MathContext _mathContext =
new MathContext (120, BigDecimal.ROUND_HALF_UP);
}
答案 2 :(得分:3)
有没有办法为所有BigDecimal计算设置'全局准确度'?
没有
您必须创建一个包含MathContext
作为额外属性的包装类。它需要:
对每个使用默认语义的数学运算使用此mc
,
每次包装操作返回常规实例时,创建并返回另一个包装实例。
(作为变体,您可以使用静态实现'全局'MathContext
,但您仍需要使用包装来确保使用mc
。)
(扩展BigDecimal
也会起作用,这比包装类更有说服力。)
你在评论中这样说:
我真的不想写自己的Decimal模块,我只想了解为什么BigDecimal如此不合作。
(设计问题只能由设计团队明确回答。但是......)
与所有复杂的实用程序类一样,BigDecimal的设计是一种折衷方案,旨在满足各种用例的要求。它也是“强大”和“简单”的竞争元要求(错误的词)之间的妥协。
您所拥有的是一个不太受支持的用例。但我怀疑如果得到很好的支持(例如,全局MathContext
控制所有内容或MathContext
附加到每个BigDecimal
)那么这将引入各种其他复杂性;例如处理有两个或多个竞争上下文对象要考虑的操作。这些问题可以解决......但它们可能会给程序员带来“惊喜”,这不是一件好事。
当前的方法简单易懂,如果您需要更复杂的东西,您可以通过为需要它的操作明确提供MathContext
来实现它。
答案 3 :(得分:2)
您可以创建一个扩展BigDecimal
的类,并自动为您设置精度。然后你只需要使用那个课程。
public class MyBigDecimal extends BigDecimal {
public MyBigDecimal(double d) {
super(d);
this.setScale(120, BigDecimal.ROUND_HALF_UP);
}
...
}
答案 4 :(得分:0)
您可以为BigDecimals创建一个包装器,它将执行此任务:
class BigDecimalWrapper {
BigDecimal bd;
BigDecimalWrapper add(BigDecimalWrapper another) {
BigDecimal r = this.bd.add(another.bd);
r.setScale(...);
bd = r;
return this;
}
// and so on for other operations
}
在这种情况下,您不必覆盖BigDecimal的所有操作(在扩展的情况下),只是您使用的操作。它使您可以控制所有实例,并且不会强制遵循BigDecimal合同。
答案 5 :(得分:0)
您必须创建自己的包装类,该包装类具有默认的MathContext,例如完整的示例:
在此示例中,我使用了库(https://projectlombok.org)
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BigDecimalFactory {
//Seguindo a precisão máxima do PowerBuilder e Oracle Database
public static final MathContext DEFAULT_CONTEXT = new MathContext(120 , RoundingMode.HALF_UP);
private static final BigDecimalFactory FACTORY = new BigDecimalFactory();
private class SBigDecimal extends BigDecimal {
public SBigDecimal(BigDecimal bdNumber) {
super(bdNumber.toPlainString());
}
public SBigDecimal(String stringNumber) {
super(stringNumber);
}
@Override
public BigDecimal divide(BigDecimal divisor) {
return new SBigDecimal(super.divide(divisor, DEFAULT_CONTEXT).stripTrailingZeros());
}
@Override
public BigDecimal divide(BigDecimal divisor, MathContext mc) {
return new SBigDecimal(super.divide(divisor, mc));
}
@Override
public BigDecimal divideToIntegralValue(BigDecimal divisor) {
return new SBigDecimal(super.divideToIntegralValue(divisor));
}
@Override
public BigDecimal divideToIntegralValue(BigDecimal divisor, MathContext mc) {
return new SBigDecimal(super.divideToIntegralValue(divisor, mc));
}
@Override
public BigDecimal remainder(BigDecimal divisor) {
return new SBigDecimal(super.remainder(divisor));
}
@Override
public BigDecimal remainder(BigDecimal divisor, MathContext mc) {
return new SBigDecimal(super.remainder(divisor, mc));
}
@Override
public BigDecimal pow(int n) {
return new SBigDecimal(super.pow(n));
}
@Override
public BigDecimal pow(int n, MathContext mc) {
return new SBigDecimal(super.pow(n, mc));
}
@Override
public BigDecimal abs() {
return new SBigDecimal(super.abs());
}
@Override
public BigDecimal abs(MathContext mc) {
return new SBigDecimal(super.abs(mc));
}
@Override
public BigDecimal negate() {
return new SBigDecimal(super.negate());
}
@Override
public BigDecimal negate(MathContext mc) {
return new SBigDecimal(super.negate(mc));
}
@Override
public BigDecimal plus() {
return new SBigDecimal(super.plus());
}
@Override
public BigDecimal plus(MathContext mc) {
return new SBigDecimal(super.plus(mc));
}
@Override
public BigDecimal round(MathContext mc) {
return new SBigDecimal(super.round(mc));
}
@Override
public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
return new SBigDecimal(super.setScale(newScale, roundingMode));
}
@Override
public BigDecimal setScale(int newScale, int roundingMode) {
return new SBigDecimal(super.setScale(newScale, roundingMode));
}
@Override
public BigDecimal setScale(int newScale) {
return new SBigDecimal(super.setScale(newScale));
}
@Override
public BigDecimal movePointLeft(int n) {
return new SBigDecimal(super.movePointLeft(n));
}
@Override
public BigDecimal movePointRight(int n) {
return new SBigDecimal(super.movePointRight(n));
}
@Override
public BigDecimal scaleByPowerOfTen(int n) {
return new SBigDecimal(super.scaleByPowerOfTen(n));
}
@Override
public BigDecimal stripTrailingZeros() {
return new SBigDecimal(super.stripTrailingZeros());
}
@Override
public BigDecimal min(BigDecimal val) {
return new SBigDecimal(super.min(val));
}
@Override
public BigDecimal max(BigDecimal val) {
return new SBigDecimal(super.max(val));
}
@Override
public BigDecimal ulp() {
return new SBigDecimal(super.ulp());
}
@Override
public BigDecimal add(BigDecimal augend, MathContext mc) {
return new SBigDecimal(super.add(augend, mc));
}
@Override
public BigDecimal subtract(BigDecimal subtrahend) {
return new SBigDecimal(super.subtract(subtrahend));
}
@Override
public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) {
return new SBigDecimal(super.subtract(subtrahend, mc));
}
@Override
public BigDecimal multiply(BigDecimal multiplicand) {
return new SBigDecimal(super.multiply(multiplicand));
}
@Override
public BigDecimal multiply(BigDecimal multiplicand, MathContext mc) {
return new SBigDecimal(super.multiply(multiplicand, mc));
}
@Override
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
return new SBigDecimal(super.divide(divisor, scale, roundingMode));
}
@Override
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
return new SBigDecimal(super.divide(divisor, scale, roundingMode));
}
@Override
public BigDecimal divide(BigDecimal divisor, int roundingMode) {
return new SBigDecimal(super.divide(divisor, roundingMode));
}
@Override
public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) {
return new SBigDecimal(super.divide(divisor, roundingMode));
}
@Override
public BigDecimal add(BigDecimal augend) {
return new SBigDecimal(super.add(augend));
}
}
public BigDecimal internalCreate(String stringNumber) {
return new SBigDecimal(stringNumber);
}
public static BigDecimal create(BigDecimal b) {
return FACTORY.internalCreate(b.toString());
}
public static BigDecimal create(String stringNumber) {
return FACTORY.internalCreate(stringNumber);
}
public static BigDecimal create(Long longNumber) {
return FACTORY.internalCreate(longNumber.toString());
}
public static BigDecimal create(Integer doubleNumber) {
return FACTORY.internalCreate(doubleNumber.toString());
}
public static BigDecimal create(Double doubleNumber) {
return FACTORY.internalCreate(doubleNumber.toString());
}
}
测试:
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class JavaTeste {
public static void main(String args[]){
//1) With your own BigDecimal
BigDecimal b1 = BigDecimalFactory.create("100");
BigDecimal b2 = BigDecimalFactory.create("25");
BigDecimal b3 = BigDecimalFactory.create("13");
System.out.println(b1.divide(b2));
System.out.println(b1.divide(b3));
//2) Without your own BigDecimal
MathContext mathContext = new MathContext(38, RoundingMode.HALF_UP);
BigDecimal b01 = new BigDecimal("100", mathContext);
BigDecimal b02 = new BigDecimal("25", mathContext);
BigDecimal b03 = new BigDecimal("13", mathContext);
System.out.println(b01.divide(b02));
System.out.println(b01.divide(b03, mathContext));
//3) And this just not work
BigDecimal b001 = new BigDecimal("100");
BigDecimal b002 = new BigDecimal("25");
BigDecimal b003 = new BigDecimal("13");
System.out.println(b001.divide(b002));
System.out.println(b001.divide(b003));
}
}
答案 6 :(得分:-4)
您可以使用BigDecimal setScale函数!
BigDecimal db = new BigDecimal(<number>).setScale(120, BigDecimal.ROUND_HALF_UP); (or down)