将所有BigDecimal操作设置为一定的精度?

时间:2012-04-08 03:29:42

标签: java math rounding bigdecimal floating-accuracy

我的Java程序以高精度计算为中心,需要精确到至少120个小数位 因此,程序中的所有非整数都将由BigDecimals表示。

显然我需要指定BigDecimals舍入的准确性,以避免无限小数表达式等。
目前,我发现必须在BigDecimal的每个实例化或数学运算中指定准确性是一件非常麻烦的事。

有没有办法设定全球准确度'对于所有BigDecimal计算?
(例如python中 Decimal 模块的Context.prec()

由于


规格:
Java jre7 SE
Windows 7(32)

7 个答案:

答案 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)