Search code examples
javabigdecimal

BigDecimal and scaling for prices


I am trying to figure out what is the best way to have two decimal scaling for prices.

So here is the scenario. Lets say I have full price of 100.00 and after discount you pay 90. So the discount percent is 10%. To achieve this I wrote something like, which works fine

BigDecimal grossPrice = new BigDecimal(100);
BigDecimal discountedPrice = new BigDecimal(90);
BigDecimal.ONE.subtract(discountedPrice.divide(grossPrice,2, RoundingMode.HALF_EVEN))
            .multiply(BigDecimal.valueOf(100))
            .setScale(2, RoundingMode.HALF_EVEN)
            .doubleValue();

But as soon as I change discountedPrice to 89.5, and I expect discount percent to 10.5, but I still get 10 and the reason is clear because 89.5/100 gives 0.895 and since its half even rounds it to 0.9 and so still 10%

If i do HALF_UP, its as good as half_even. If i do, HALF_DOWN, the value will be 0.89 and I will have discount percent as 11. So I am bit confuse as to what will actually give me 10.5% discount in this case.


Solution

  • How about setting scale to 3. Remember to change it to both places:

    • At where you divide,
    • At where you round to half even.
    BigDecimal grossPrice = new BigDecimal(100);
    BigDecimal discountedPrice = new BigDecimal("89.5");
    double doubleValue = BigDecimal.ONE
            .subtract(discountedPrice.divide(grossPrice,3, RoundingMode.HALF_EVEN))  // here
            .multiply(BigDecimal.valueOf(100))
            .setScale(3, RoundingMode.HALF_EVEN)                                     // here
            .doubleValue();
    
    System.out.println(doubleValue);                                                 // 10.5
    

    You might want to define MathContext to support your calculations and avoid typos:

    MathContext halfEvenTo3 = new MathContext(3, RoundingMode.HALF_EVEN);
    
    BigDecimal grossPrice = new BigDecimal(100);
    BigDecimal discountedPrice = new BigDecimal("89.5");
    double doubleValue = BigDecimal.ONE
            .subtract(discountedPrice.divide(grossPrice, halfEvenTo3))              // here
            .multiply(BigDecimal.valueOf(100))
            .round(halfEvenTo3)                                                     // here
            .doubleValue();
    
    System.out.println(doubleValue);                                                // 10.5