JDK-8205592 : BigDecimal.doubleValue() is depressingly slow
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.math
  • Affected Version: 8,9,10,11
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2018-06-23
  • Updated: 2018-06-26
Related Reports
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
I find the following slow path in BigDecimal:

        // Somewhat inefficient, but guaranteed to work.
        return Double.parseDouble(this.toString())

Since JDK-7131192 : BigInteger.doubleValue() is depressingly slow
is now available, something faster could be done.

It suggest doing unscaledValue().doubleValue() * Math.exp(scale()*Math.log(10))



Comments
Note that the results of the current BigDecimal code will likely change in some cases once the fix for JDK-8202555 is in.
26-06-2018

Additional information from submitter : I have a running prototype, which has a different performance than the actual doubleValue() implementation for BigDecimal. The idea is not to use Math.exp(scale()*Math.log(10)), but to make the following observation, first from old JDK-7131192: For BigInteger values we could implement: int getExponent(BigInteger) long getMantissa(BigInteger) double packDouble(int, long) We can then transform a BigDecimal into a BigInteger, and determine the mantissa from there, but need to adapt the exponent. So when we have a BigDecimal: unscaledValue * 10^(-scale) There are two cases: Case 1: scale>0 compute unscaledValue' as (unscaledValue<<3*scale)/5^scale we then have for the BigDecimal: getExponent(BigDecimal) = getExponent(unscaledValue')-4*scale getMantissa(BigDecimal) = getMantissa(unscaledValue') Case 2:scale=<0 compute unscaledValue' as unscaledValue*5^scale we then have for the BigDecimal: getExponent(BigDecimal) = getExponent(unscaledValue')-scale getMantissa(BigDecimal) = getMantissa(unscaledValue') I cannot yet upload some code. There are still some small open issues for Case 1, how many extra bits should be generated and how the rounding of the division should be done there. But basically it worked, and I already did some benchmarking, and the algorithm is faster sometimes. The algorithm doesn't beat always the toString()/parse() approach, since by inspection of the parse() method, this method uses a large 5^exp cache. So for large scale values our new method, which uses the ordinary BigInteger.pow() has a slight disadvantage. But one might consider also creating a cache here. I will try to publish code and benchmark results when finished.
25-06-2018