java 将所有 BigDecimal 操作设置为特定精度?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10060158/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 23:25:52  来源:igfitidea点击:

Set all BigDecimal operations to a certain precision?

javamathroundingbigdecimalfloating-accuracy

提问by Anti Earth

My Java program is centered around high precision calculations, which need to be accurate to at least 120 decimal places.
Consequentially, all non-integer numbers will be represented by BigDecimals in the program.

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

Obviously I need to specify the accuracy of the rounding for the BigDecimals, to avoid infinite decimal expressions etc.
Currently, I find it a massive nuisance to have to specify the accuracy at every instantiation or mathematical operation of a BigDecimal.

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

Is there a way to set a 'global accuracy' for all BigDecimal calculations?
(Such as the Context.prec()for the Decimalmodule in python)

有没有办法为所有 BigDecimal 计算设置“全局精度”?
(比如python中Context.prec()Decimal模块)

Thanks

谢谢



Specs:
Java jre7 SE
Windows 7 (32)

规格:
Java jre7 SE
Windows 7 (32)

采纳答案by trutheality

(Almost) Original

(几乎)原创

Not as simple, but you can create a MathContextand pass it to all your BigDecimalconstructors and the methods performing operations.

不是那么简单,但您可以创建一个MathContext并将其传递给所有BigDecimal构造函数和执行操作的方法。

Revised

修改

Alternatively, you can extend BigDecimaland override any operations you want to use by supplying the right MathContext, and using the rounding version of divide:

或者,您可以BigDecimal通过提供 rightMathContext并使用 的舍入版本来扩展和覆盖您想要使用的任何操作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 ) );
      }
      ...
}

回答by sparc_spread

Create a BigDecimalFactoryclass with static factory methods matching all constructors that accept MathContext- except that the MathContextinstance is inside the factory and statically initialized at startup time. Here's a fragment:

创建一个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);
}

回答by Stephen C

Is there a way to set a 'global accuracy' for all BigDecimal calculations?

有没有办法为所有 BigDecimal 计算设置“全局精度”?

No.

不。

You'll have to create a wrapper class that has a MathContextas an extra attribute. It will need to:

您必须创建一个具有MathContext作为额外属性的包装类。它需要:

  • use this mcfor each mathematical operation that would otherwise use the default semantics, and

  • create and return another wrapped instance each time the wrapped operation returns a regular instance.

  • 将此mc用于否则将使用默认语义的每个数学运算,以及

  • 每次包装操作返回一个常规实例时,创建并返回另一个包装实例。

(As a variation, you could implement a 'global' MathContextusing a static, but you'll still need to use wrappering to ensure that the mcis used.)

(作为一种变体,您可以MathContext使用静态实现“全局” ,但您仍然需要使用包装来确保mc使用 。)

(Extending BigDecimalwould work too, and that is arguable neater than a wrapper class.)

(扩展BigDecimal也可以,这比包装类更整洁。)



You said this in a comment:

你在评论中说:

I really don't want to write my own Decimal module, I just want to understand why BigDecimal is being so uncooperative.

我真的不想编写自己的 Decimal 模块,我只是想了解为什么 BigDecimal 如此不合作。

(Design questions can only be answered definitivelyby the design team. However ...)

(设计问题只能由设计团队明确回答。但是......)

As with all complicated utility classes, the design of BigDecimal is a compromise that is designed to meet the requirements of a wide range of use-cases. It is also a compromise between the competing meta-requirements (wrong word) of "powerfulness" and "simplicity".

与所有复杂的实用程序类一样,BigDecimal 的设计是一种折衷方案,旨在满足各种用例的要求。它也是“强大”和“简单”这两个相互竞争的元需求(错误的词)之间的妥协。

What you have is a use-case that is not particularly well supported. But I suspect that if it was well supported (e.g. with a global MathContextcontrolling everything or a MathContextattached to each BigDecimal) then that would introduce all sorts of other complexities; e.g. dealing with operations where there are two or more competing context objects to consider. Such problems could be dealt with ... but they are liable to lead to "surprises" for the programmer, and that is not a good thing.

您拥有的是一个不受特别支持的用例。但我怀疑,如果它得到很好的支持(例如使用全局MathContext控制一切或MathContext附加到每个BigDecimal),那么这将引入各种其他复杂性;例如,处理有两个或更多竞争上下文对象要考虑的操作。这样的问题是可以处理的……但它们很容易给程序员带来“惊喜”,这不是一件好事。

The current approach is simple and easy to understand, and if you need something more complicated you can implement it ... by explicitly supplying a MathContextfor the operations that require it.

当前的方法简单易懂,如果您需要更复杂的东西,您可以实现它……通过MathContext为需要它的操作显式提供 a 。

回答by twain249

You could create a class that extends BigDecimaland sets the precision automatically for you. Then you just use you that class.

您可以创建一个BigDecimal自动为您扩展和设置精度的类。然后你就用你那个班级。

public class MyBigDecimal extends BigDecimal {
      public MyBigDecimal(double d) {
           super(d);
           this.setScale(120, BigDecimal.ROUND_HALF_UP);
      }
      ...
}

回答by Eugene Retunsky

You can create a wrapper for BigDecimals, which will do this job:

您可以为 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
 }

In this case you don't have to override all operation of BigDecimal (in extending case), just ones you use. It gives you control over all instances and doesn't force to follow BigDecimal contract.

在这种情况下,您不必覆盖 BigDecimal 的所有操作(在扩展情况下),只需使用您使用的操作即可。它使您可以控制所有实例,并且不会强制遵循 BigDecimal 合同。

回答by rafambbr

You'll have to create your own wrapper class that has your default MathContext like this full example:

您必须创建自己的包装类,该类具有您的默认 MathContext,如以下完整示例:

In this example, I used the library (https://projectlombok.org)

在这个例子中,我使用了库(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());
    }

}

Test:

测试:

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

    }

}

回答by Kevin

You can use the BigDecimal setScale function!

您可以使用 BigDecimal setScale 函数!

BigDecimal db = new BigDecimal(<number>).setScale(120, BigDecimal.ROUND_HALF_UP); (or down)