I'd like to use String.format() to format some BigDecimals as part of a string:
// Example:
String getPrice( String pattern )
{
BigDecimal price = basePrice.multiply( BigDecimal.ONE.add( vatRate ) );
BigDecimal priceInPence = price.multiply( new BigDecimal( "100" ) );
BigDecimal annualPrice = price.multiply( new BigDecimal( "365" ) );
return String.format( pattern, priceInPence, annualPrice );
}
String myPrice1 = getPrice( "Your price is %1$.3fp/day (£2$.2f/year) including VAT" );
// --> "Your price is 32.100p/day (£117.16/year) including VAT"
String myPrice2 = getPrice( "Around £%2$.0f annualy" );
// --> "Around £117 annually"
However the docs for String.format() say that any rounding of BigDecimals will be done with HALF_UP rounding, whereas I need HALF_EVEN.
I know how to manually set the scale of BigDecimals (Set specific precision of a BigDecimal) - but in this case I want to be able to use an arbitrary pattern string (including non-numeric pattern elements), so I won't know in advance what scale to use.
My question is therefore:
Really "solid" advice to add 5000 lines of dead-weight code to your project! From what I see, the Formatter will not set scale unless it is already set to what is needed. So help it out, parse the format string and set your scale:
public static String getPrice(String pattern) {
BigDecimal basePrice = new BigDecimal("23");
BigDecimal vatRate = new BigDecimal("0.5");
BigDecimal price = basePrice.multiply(BigDecimal.ONE.add(vatRate));
BigDecimal priceInPence = price.multiply(new BigDecimal("100"));
BigDecimal annualPrice = price.multiply(new BigDecimal("365"));
Matcher matcher = Pattern.compile("%(\\d+)\\$.(\\d+)f").matcher(pattern);
while (matcher.find()) {
String index = matcher.group(1);
int scale = Integer.parseInt(matcher.group(2));
if (index.equals("1"))
priceInPence = priceInPence.setScale(scale, RoundingMode.HALF_EVEN);
else if (index.equals("2"))
annualPrice = annualPrice.setScale(scale, RoundingMode.HALF_EVEN);
}
return String.format(pattern, priceInPence, annualPrice);
}
with these numbers I get this output:
Your price is 3450.000p/day (£12592.50/year) including VAT
Around £12592 annualy
So it applies correct rounding.
can I set the rounding mode used by String.format()?
Short answer: no.
is there another formatter or library that would format the numbers as in my example?
The BigDecimal is converted internally via new MathContext(compPrec)
or plain HALF_UP
.
You can take the code of java.util.Formatter
of the latest (or your preferred) version Java and modify the creation of the MathContext to use HALF_EVEN. It should be 10-15minutes work. But then you need a custom method to mimic String.format
:
public static String format(String format, Object... args) {
return new FormatterHALF_EVEN().format(format, args).toString();
}
Set the scale with the rounding mode you like, and include the values in the format string as strings, using BigDecimal#toString()
.