保持最大精度水平

时间:2019-04-16 08:07:43

标签: java

我正在研究业务逻辑,我需要将BigDecimal变量相除并相乘以产生业务结果,但是我面临着保持准确性的问题。

我无法在这里进行实际交易,因此我创建了一个示例程序并将其包含在此处。我只需要使用BigDecimal,所以我对此非常严格,但是我愿意使用任何比例尺,模式或任何有助于我获得最大精度的东西。

始终欢迎您提出建议。

示例代码

public class Test {
    public static void main(String[] args) {
        BigDecimal hoursInADay = new BigDecimal("24");
        BigDecimal fraction = BigDecimal.ONE.divide(hoursInADay, 3,
                RoundingMode.HALF_UP);
        BigDecimal count = BigDecimal.ZERO;

        for (int i = 1; i <= 24; i++) {
            count = count.add(fraction);
        }

        if (BigDecimal.ONE.equals(count)) {
            // accuracy level 100%
        }
    }
}

2 个答案:

答案 0 :(得分:3)

只是一个实验(很有趣),我试图实现这个Fraction类,该类包装了BigDecimal但避免了除法直到需要最终结果。

方法实现基于:

  • 添加:a/b + c/d = (ad + bc)/bd
  • 相乘:(a/b) * (c/d) = ac/bd
  • 除法:(a/b)/(c/d) = ad/bc

之所以不使用它是因为BigDecimal的准确性不足,而是因为过早的除法必然会在无终止值的情况下导致舍入误差。

代码:

class Fraction {

    private final BigDecimal numerator;
    private final BigDecimal denominator;

    public Fraction(BigDecimal numerator, BigDecimal denumerator) {
        this.numerator = numerator;
        this.denominator = denumerator;
    }

    public static final Fraction ZERO = new Fraction(BigDecimal.ZERO, 
                            BigDecimal.ONE);
    public static final Fraction ONE = new Fraction(BigDecimal.ONE, 
                            BigDecimal.ONE);

    public static Fraction of(BigDecimal numerator) {
        return new Fraction(numerator, BigDecimal.ONE);
    }

    public static Fraction of(BigDecimal numerator, BigDecimal denominator) {
        return new Fraction(numerator, denominator);
    }

    public Fraction add(Fraction other) {
        return Fraction.of(other.denominator.multiply(this.numerator)
                                .add(other.numerator.multiply(this.denominator)),
                           this.denominator.multiply(other.denominator));
    }

    public Fraction multiply(Fraction other) {
        return new Fraction(this.numerator.multiply(other.numerator), 
                            this.denominator.multiply(other.denominator));
    }

    public Fraction divide(Fraction other) {
        return new Fraction(this.numerator.multiply(other.denominator), 
                            this.denominator.multiply(other.numerator));
    }

    public BigDecimal value() {
        try {
            return this.numerator.divide(this.denominator);
        } catch (ArithmeticException ae) {
            return this.numerator.divide(this.denominator, 6, 
                        RoundingMode.HALF_UP);
        }
    }

    @Override
    public String toString() {
        return String.format("%s/%s", this.numerator, this.denominator);
    }
}

并使用它来执行原始计算:

public static void main(String[] args) {
    Fraction twentyFour = Fraction.of(BigDecimal.valueOf(24));
    Fraction fraction = Fraction.ONE.divide(twentyFour);
    System.out.println("Fraction = " + fraction);

    Fraction count = new Fraction(BigDecimal.ZERO, BigDecimal.ONE);
    for (int i = 1; i <= 24; i++) {
        count = count.add(fraction);
    }

    if (BigDecimal.ONE.equals(count.value())) {
        System.out.println("100%");
    } else {
        System.out.println(count);
    }
}

输出:

Fraction = 1/24
100%

重要的是要注意,这绝不是优化的。例如,分数是 not 简化的({1/24 + 1/24将存储为48/576而不是1/12,并且可能具有不可忽略的存储和计算成本)

答案 1 :(得分:0)

我不确定这是否有意义,但我只会在您的代码中发布一些更改

public static void main(String[] args) {
    BigDecimal hoursInADay = new BigDecimal("24");
    BigDecimal fraction = BigDecimal.ONE.divide(hoursInADay, MathContext.DECIMAL64.getPrecision(),
            RoundingMode.HALF_UP);
    BigDecimal count = BigDecimal.ZERO;

    for (int i = 1; i <= 24; i++) {
        count = count.add(fraction);
    }
    count = count.round(MathContext.DECIMAL32);
    System.out.println(BigDecimal.ONE.compareTo(count) == 0);
}

除法运算比实际结果所需的精度更高。当然,这些数字是根据您的需要进行调整的。那应该(?)为您提供更精确的最终结果。我还使用了compareTo方法,因为当1方法不使用时,它将1.000equals视为相等的数字。

那只是一个命题,想知道您对此怎么想。