A BigDecimal accumulator for Drools

Working in the financial industry, I have become rather strict about avoiding doubles in Java. The trouble is that they are a floating point representation of a number, which is just an approximation of the real value. This can lead to some unusual results.

For instance, according to this, 0.34 + 0.01 is not equal to 0.35.

double x = 0.35;
double y = 0.34 + 0.01;
System.out.println(x + " : " + y + " : " + (x == y));
0.35 : 0.35000000000000003 : false

Those inaccuracies might seem very small, but it’s surprisingly easy for them to start impacting a real world application. Imagine you wanted to sell dollars and buy Iranian Rial. You would be getting almost 20,000 Rial for every dollar. At that rate, imprecise floating point values could easily impact the final amount being sent. Although with the current US trade sanctions against Iran, that could be the least of your problems.

If you are running reports on a history of transactions, then a large number of smaller transactions can add up to large enough values that the imprecise doubles start affecting your totals. Even if you’re not dealing in huge numbers, things can go wrong easily enough. If your process takes a number through a sequence of multiplications or divisions, errors can be magnified. If you have a business rule to always round up, then according to our calculation above, 0.34 + 0.01 = 0.36.

However, that’s enough about why doubles are bad for financial calculations. What caught me by surprise was running accumulate functions in Drools. I was writing code to react to currency exposures being at particular limits, which were all being added up from nice healthy BigDecimal values. However, the numbers I was getting from the ‘sum’ accumulate function were not equal to the numbers my unit tests were expecting. A little investigation showed that the sum accumulator was converting all my nice fixed precision BigDecimal numbers into doubles. Oh dear…

So after a little further investigation, I established that there are no BigDecimal accumulators for Drools, and a bug has been open since 2008. The only workaround mentioned was to write your own ‘sumBigDecimal’ accumulator function.

This didn’t seem like great progress, but I thought it seemed like a good opportunity to learn a new corner of Drools, so I knocked together this BigDecimalAccumulator implementation:

package uk.co.scattercode.drools.accumulators;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.math.BigDecimal;
import org.drools.runtime.rule.TypedAccumulateFunction;
/**
* This is a 'sum' accumulator for a collection of {@link BigDecimal}.
* Out-of-the-box Drools has sum accumulators which are able to add up
* BigDecimals, but do so by converting them to doubles. So the number you get
* out at the end is not good if your application is doing anything
* financial.
*
* To use this, create a package builder configuration file:
* <pre>
* META-INF/drools.packagebuilder.conf
* </pre>
* Put the following in it:
* <pre>
* drools.accumulate.function.sumbd = uk.co.scattercode.drools.accumulators.BigDecimalAccumulator
* </pre>
* And you can now do things like:
* <pre>
* accumulate(
* CurrencyExposure( currency in ("GBP", "EUR", "NOK"),
* $exp : exposure
* ),
* $sumGbpEurNok : sumbd( $exp )
* )
* </pre>
*
* @author Stephen Masters
*/
public class BigDecimalAccumulator implements TypedAccumulateFunction {
/**
* Session-specific data required by the accumulator is stored in a
* {@link BigDecimalSum} context, which is instantiated by this
* method.
*/
@Override
public Serializable createContext() {
return new BigDecimalSum();
}
/**
* Initializes the accumulator with an empty list of {@link BigDecimal}.
*/
@Override
public void init(Serializable context) throws Exception {
BigDecimalSum accumulator = (BigDecimalSum) context;
accumulator.init();
}
/**
* Adds the value to the accumulator sum.
*/
@Override
public void accumulate(Serializable context, Object value) {
BigDecimalSum accumulator = (BigDecimalSum) context;
accumulator.add((BigDecimal) value);
}
/**
* Subtracts the value from the accumulator sum.
*/
@Override
public void reverse(Serializable context, Object value) throws Exception {
BigDecimalSum accumulator = (BigDecimalSum) context;
accumulator.subtract((BigDecimal) value);
}
/**
* Yes, this accumulator does implement the reverse method..
*/
@Override
public boolean supportsReverse() {
return true;
}
/**
* Returns the current 'sum' held in the accumulator.
*/
@Override
public Object getResult(Serializable context) throws Exception {
BigDecimalSum accumulator = (BigDecimalSum) context;
return accumulator.sum;
}
/**
* Returns the class of the object returned by getResult.
*/
@Override
public Class<BigDecimal> getResultType() {
return BigDecimal.class;
}
/**
* Required to support {@link Externalizable} interface so that data can be
* shared across sessions. However, we don't need to do that, so this method
* is empty.
*/
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
/**
* Required to support {@link Externalizable} interface so that data can be
* shared across sessions. However, we don't need to do that, so this method
* is empty.
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
/**
* Session-specific data required by the accumulator is stored in a
* an instance of this class.
*/
private static class BigDecimalSum implements Serializable {
/** Generated serialVersionUID */
private static final long serialVersionUID = 3852330030144129793L;
BigDecimal sum = BigDecimal.ZERO;
void init() {
this.sum = BigDecimal.ZERO;
}
void add(BigDecimal augend) {
this.sum = sum.add(augend);
}
void subtract(BigDecimal subtrahend) {
this.sum = sum.subtract(subtrahend);
}
}
}

I am a bit puzzled that I’m not finding examples of this all over the place, as Drools has seen a lot of uptake in the financial industry, and it seems like an obvious thing that anybody using Drools for financial rules and calculations would need. Maybe there are loads of private repositories out there, each with their own implementations?

Anyway, in the absence of BigDecimal accumulator functionality in core Drools, feel free to grab this for your own applications.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.