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%
}
}
}
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:
a/b + c/d = (ad + bc)/bd
(a/b) * (c/d) = ac/bd
(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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With