Search code examples
javaperformancefloating-pointbigdecimalvalue-of

Performance of BigDecimal.valueOf( double d )


I have a piece of code that needs to repetitively calculate the following...

        double consumption = minConsumption + ( Math.random() * ( ( maxConsumption - minConsumption ) + 1 ) );
        currentReading = currentReading.add( BigDecimal.valueOf( consumption ) ).setScale( 2, RoundingMode.HALF_EVEN ).stripTrailingZeros();

This is used for generating random-ish information for testing. It seemed to be running slower than I would expect and I found that the slow part was BigDecimal.valueOf( consumption ) and that is slow because of the Double.toString() call that is happening internally.

The overall requirement is to generate a consumption value that is randomly between a minimum and maximum. Then add that to the currentReading to get a new reading.

Is there any way I can improve the performance of this? Perhaps by avoiding the double -> BigDecimal conversion. I need to the result to be a BigDecimal but I don't mind how the random calculation is done prior to that.


Solution

  • Instead of calculating a double you want to round to two decimal places, you can create an int value which is shifted by two places i.e. where 1234 represents 12.34 and then set the scale when you create the BigDecimal. i.e. dividing it by 100

    double min = 100;
    double max = 10000000;
    {
        long start = 0;
        int runs = 1000000;
        for (int i = -10000; i < runs; i++) {
            if (i == 0)
                start = System.nanoTime();
            double consumption = min + (Math.random() * ((max - min) + 1));
    
            BigDecimal.valueOf(consumption).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
        long time = System.nanoTime() - start;
        System.out.printf("The average time with BigDecimal.valueOf(double) was %,d%n", time / runs);
    }
    {
        long start = 0;
        int runs = 1000000;
        int min2 = (int) (min * 100);
        int range = (int) ((max - min) * 100);
        Random rand = new Random();
        for (int i = -10000; i < runs; i++) {
            if (i == 0)
                start = System.nanoTime();
            int rand100 = rand.nextInt(range) + min2;
            BigDecimal bd = BigDecimal.valueOf(rand100, 2);
        }
        long time = System.nanoTime() - start;
        System.out.printf("The average time with BigDecimal.valueOf(long, int) was %,d%n", time / runs);
    
    }
    

    prints

    The average time with BigDecimal.valueOf(double) was 557
    The average time with BigDecimal.valueOf(long, int) was 18