I'm connecting to an external system that provides me with a stream of double financial prices. I know that the values I receive are meant to be decimal values, and that therefore BigDecimal would have been a better choice, but the provided client library forces me to use double instead.
My goal is to build a BigDecimal from the provided double values. The simple solution is to just use BigDecimal.valueOf(double). However, as is a common problem when working with double, the values that I receive look like 0.12000000001 instead of 0.12, or 1.15999999998 instead of 1.16. I could do some rounding by using BigDecimal.setScale(), but the problem with that is that I don't know the exact intended precision of the value.
How can I reconstruct the intended decimal value?
My solution so far relies on the fact that the value I receive is always within 1 or 2 ULP of the intended decimal value. So I just try converting the value I receive as well as the values 1-2 ulp up/down to String, and create a BigDecimal from the shortest of these strings.
private static BigDecimal shortestBigDecimal(double inputDouble) {
double ulp = Math.ulp(inputDouble);
String center = Double.toString(inputDouble);
String high = Double.toString(inputDouble + ulp);
String low = Double.toString(inputDouble - ulp);
String high2 = Double.toString(inputDouble + ulp + ulp);
String low2 = Double.toString(inputDouble - ulp - ulp);
String shortest = minLength(minLength(minLength(minLength(low2, low), center), high), high2);
return new BigDecimal(shortest);
}
private static String minLength(String a, String b) {
return a.length() < b.length() ? a : b;
}
This code passes all of my test cases, but it creates a lot of temporary Strings.
Is there a more efficient way to do this?