【问题标题】:Java String.format() with HALF_EVEN rounding带有 HALF_EVEN 舍入的 Java String.format()
【发布时间】:2014-11-09 23:10:48
【问题描述】:

我想使用 String.format() 将一些 BigDecimal 格式化为字符串的一部分:

// 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"

但是,String.format() 的 docs 表示 BigDecimals 的任何舍入都将使用 HALF_UP 舍入完成,而我需要 HALF_EVEN。

我知道如何手动设置 BigDecimals 的比例 (Set specific precision of a BigDecimal) - 但在这种情况下,我希望能够使用任意模式字符串(包括非数字模式元素),所以我不知道提前使用什么比例。

因此我的问题是:

  • 我可以设置 String.format() 使用的舍入模式吗?或者

  • 是否有其他格式化程序或库可以像我的示例那样格式化数字?

【问题讨论】:

  • 看看 java.math.MathContext。您可以使用 java.math.RoundingMode (HALF_EVEN) 构造 MathContext,然后将其传递给 BigDecimal 的构造函数或乘法方法之一:multiply(BigDecimal multiplicand, MathContext mc)
  • @mttdbrd - 抱歉,我应该根据模式明确我需要任意精度(参见我上面的编辑)。
  • @ComputerFellow - 抱歉,这些是关于 BigDecimals 的答案,我的问题是关于更改 String.Format() 使用的舍入模式

标签: java string-formatting formatter


【解决方案1】:

我可以设置 String.format() 使用的舍入模式吗?
简短的回答:没有。

是否有其他格式化程序或库可以像我的示例那样格式化数字?
BigDecimal 通过new MathContext(compPrec) 或普通HALF_UP 在内部进行转换。 您可以获取最新(或您喜欢的)Java 版本的java.util.Formatter 的代码,并修改 MathContext 的创建以使用 HALF_EVEN。应该是10-15分钟的工作。但是你需要一个自定义方法来模仿String.format

public static String format(String format, Object... args) {
    return new FormatterHALF_EVEN().format(format, args).toString();
}

【讨论】:

    【解决方案2】:

    真正“可靠”的建议是在您的项目中添加 5000 行无谓的代码!据我所知,格式化程序不会设置比例,除非它已经设置为需要的值。所以帮助它,解析格式字符串并设置你的比例:

    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);
    }
    

    用这些数字我得到这个输出:

    您的价格是 3450.000 便士/天(12592.50 英镑/年),含增值税

    每年大约 12592 英镑

    所以它应用了正确的舍入。

    【讨论】:

    • 我喜欢这种方法。如果将其扩展一点以处理以不同比例多次使用令牌的情况可能会更好:
    【解决方案3】:

    使用您喜欢的舍入模式设置比例,并将值包含在格式字符串 as 字符串中,使用BigDecimal#toString()

    【讨论】:

    • 问题是,直到运行时我才知道使用什么比例尺:精度信息在模式字符串中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多