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.
How about setting scale to 3
. Remember to change it to both places:
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