3

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:

  • can I set the rounding mode used by String.format()? OR

  • is there another formatter or library that would format the numbers as in my example?

4
  • Take a look at java.math.MathContext. You can construct a MathContext using a java.math.RoundingMode (HALF_EVEN), and then pass that to the constructor of BigDecimal or to one of the multiply methods: multiply(BigDecimal multiplicand, MathContext mc) Commented Oct 30, 2014 at 4:06
  • @mttdbrd - sorry, I should have made clear I need arbitrary precision, based on the pattern (see my edit above). Commented Oct 30, 2014 at 4:21
  • stackoverflow.com/questions/9482889/… and stackoverflow.com/questions/15352229/… Commented Oct 30, 2014 at 4:56
  • @ComputerFellow - sorry, those are answers about BigDecimals, my question is about changing the rounding mode used by String.Format() Commented Oct 30, 2014 at 7:39

3 Answers 3

3

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();
}
Sign up to request clarification or add additional context in comments.

Comments

1
+100

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.

1 Comment

I like this approach. It could be even better if it was extended a bit to handle the case where a token is used more than once with different scales: getPrice( "Around £%2$.0f annualy (£%2.2f to be exact)" );
0

Set the scale with the rounding mode you like, and include the values in the format string as strings, using BigDecimal#toString().

1 Comment

The problem is, I don't know what scale to use until runtime: the precision information is in the pattern String.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.