Given your rounding mode, it seems clear that you intend for the system to never end up at fractions of cents. A good plan; most financial systems cannot deal with fractional cents (you can't transfer half of a cent, or pay half a cent at the register, or even enter half a cent in a POS tool).
That means you can ditch BigDecimal entirely, and use plain jane long, which will then represent the number of 'atomic currency units'. That'd be yen for yen, cents for euros and dollars, pennies for pounds, satoshis for bitcoin, etcetera.
The numbers you are trying to represent here are completely different, and it's good to go back to what things mean, first, and then write code.
In your code, grossPrice and discountedPrice are both monetary amounts.
On the other hand, the value you are looking for isn't a monetary amount at all: It's a completely different thing - a ratio. This also shows in what you desire: For the monetary amounts, you wish to round to 2 decimals at all times, but for the ratio you don't, which is sensible - they are 2 completely different concepts.
Ratios are tricky. For example, the ratio between 1 and 3 is not perfectly representable in either base 10 (0.333333) or the base2 that e.g. double and co use. Therefore, it is not possible as a general rule to have perfect ratios. You must therefore kiss that goodbye and pick, instead, some arbitrary precision.
One easy way out is to just say: Well, hey, if perfection is no longer on the table, I'll make sure my code and documentation is written to keep that in mind, and I don't need it anymore. In which case, you might as well go with double. double is a horrible idea to represent monetary amounts with, but ratios - that's fine.. and in your code you go to double anyway.
In other words, stop using BigDecimal here: You are using it for 2 things, and in both cases, there wasn't an actual point:
- You use it to represent monetary amounts, but in a way that fractional atomic units are impossible. You can do that, but it's overengineered and needlessly complex, just use a long to store those atomic units instead.
- You use it to calculate a ratio, but you convert it to a double, so whatever fine-grained precision control you wanted to apply is undone by your conversion.
long grossPrice = 10000; // $100
long discountedPrice = 8950; // $89.50
double ratio = 100.0 * discountedPrice / grossPrice;
When printing these numbers, that's where you bring the decimals in:
System.out.printf("Ratio: %.3f\n", ratio); // print to 3 decimal places.
If you do need the fine grained control, or you want fractional atomic finance units, then please keep in mind:
- In many ways 'fractional atomic finance' is unsolvable. For example, if you want to split a 4-cent surcharge across 3 people, you just can't do that, and no amount of
BigDecimal is going to help you here: BigDecimal is incapable of perfectly representing '1 1/3 of a cent', and even if somehow it could, you can't actually charge that. The best solution would be to either round up (charge each member 2 cents), or to flip a coin, and charge 2 people 1 cent, and randomly charge one person 2 cents. There is no way to do this automatically, you'd have to code it up. In general, once division or ratios are involved, perfection is off the table.
- There are libraries for currencies, such as joda-currency. You may want to use those instead, if your aim is 'convenience' (BigDecimal isn't very convenient, hence why 'I'll use BD instead of longs; more convenient' is a weird conclsuion).
- longs CAN overflow/underflow, but that isn't relevant unless you're talking about centuries's worth of world economic output. You need a lot of money to overflow the model of 'store cents in a long'.
int however, that's no good: You can easily overflow those (40 million bucks is all you need!), so make sure to use longs.