Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maintain Accuracy Level Maximum

Tags:

java

I am working on a business logic where I need to divide and multiply BigDecimal variable to produce business result but I am facing the problem to maintain the accuracy.

Actual business I can't put here so I created a sample program and included here. I need to use only BigDecimal so I am strict to it but I am open to use any scale, mode or any thing which help me to get the maximum accuracy.

Suggestions are always welcome.

Sample Code

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%
        }
    }
}
like image 757
dom Avatar asked Oct 16 '22 16:10

dom


1 Answers

Just an experiment (and for fun), I attempted to implement this Fraction class, which wraps BigDecimal but shuns division until the final result is required.

The method implementations are based on:

  • add: a/b + c/d = (ad + bc)/bd
  • multiply: (a/b) * (c/d) = ac/bd
  • divide: (a/b)/(c/d) = ad/bc

This is not used because BigDecimal has insufficient accuracy, but because premature division necessarily leads to rounding errors in case of non-terminating values.

Code:

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

And using it to perform your original calculations:

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

Output:

Fraction = 1/24
100%

It's important to note that this is in no way optimized. For example, fractions are not simplified (1/24 + 1/24 will be stored as 48/576 instead of 1/12, and that may have a non-negligible storage and compute cost)

like image 50
ernest_k Avatar answered Oct 19 '22 00:10

ernest_k