1. 概述

本文将系统介绍Java中数字格式化的多种实现方式,涵盖基础格式化、四舍五入处理、特殊类型格式化及高级应用场景。每种方法都配有实际代码示例,助你快速掌握数字格式化的核心技巧。

2. 基础格式化:String.format()

String.format() 是最简单直接的数字格式化方法,通过格式化字符串控制小数位数:

double value = 4.2352989244d;
assertThat(String.format("%.2f", value)).isEqualTo("4.24");
assertThat(String.format("%.3f", value)).isEqualTo("4.235");

核心要点

  • 第一个参数定义格式模式(如%.2f表示保留2位小数)
  • 第二个参数传入待格式化的数值
  • 返回格式化后的字符串

3. 四舍五入格式化

Java中处理小数主要有两种基本类型:floatdouble

double myDouble = 7.8723d;
float myFloat = 7.8723f;

实际开发中通常只需保留几位小数,以下是两种常用四舍五入方案:

3.1. 使用BigDecimal实现精确控制

BigDecimal提供精确的舍入控制,推荐用于金融计算等场景:

public static double withBigDecimal(double value, int places) {
    BigDecimal bigDecimal = new BigDecimal(value);
    bigDecimal = bigDecimal.setScale(places, RoundingMode.HALF_UP);
    return bigDecimal.doubleValue();
}

使用示例:

double D = 4.2352989244d;
assertThat(withBigDecimal(D, 2)).isEqualTo(4.24);
assertThat(withBigDecimal(D, 3)).isEqualTo(4.235);

⚠️ 关键操作

  • setScale()指定小数位数和舍入模式
  • RoundingMode.HALF_UP即"四舍五入"

3.2. 使用Math.round()简单实现

通过数学运算实现快速舍入(但存在精度陷阱):

public static double withMathRound(double value, int places) {
    double scale = Math.pow(10, places);
    return Math.round(value * scale) / scale;
}

使用示例:

assertThat(withMathRound(D, 2)).isEqualTo(4.24);
assertThat(withMathRound(D, 3)).isEqualTo(4.235);

踩坑警告

System.out.println(withMathRound(1000.0d, 17)); // 输出92.23372036854776 !!
System.out.println(withMathRound(260.775d, 2)); // 输出260.77而非260.78

👉 原因Math.round()存在截断问题,仅建议学习使用,生产环境慎用!

4. 特殊类型格式化

4.1. 大整数加逗号分隔

使用DecimalFormat格式化大整数,添加千位分隔符:

public static String withLargeIntegers(double value) {
    DecimalFormat df = new DecimalFormat("###,###,###");
    return df.format(value);
}

int value = 123456789;
assertThat(withLargeIntegers(value)).isEqualTo("123,456,789");

4.2. 数字补零

固定长度数字补零(如生成订单号):

public static String byPaddingZeros(int value, int paddingLength) {
    return String.format("%0" + paddingLength + "d", value);
}

int value = 1;
assertThat(byPaddingOutZeros(value, 3)).isEqualTo("001");

4.3. 强制保留两位小数

确保数字始终显示两位小数:

public static double withTwoDecimalPlaces(double value) {
    DecimalFormat df = new DecimalFormat("#.00");
    return new Double(df.format(value));
}

int value = 12; 
assertThat(withTwoDecimalPlaces(value)).isEqualTo(12.00);

4.4. 百分比格式化

本地化百分比显示:

public static String forPercentages(double value, Locale locale) {
    NumberFormat nf = NumberFormat.getPercentInstance(locale);
    return nf.format(value);
}

double value = 25f / 100f;
assertThat(forPercentages(value, new Locale("en", "US"))).isEqualTo("25%");

4.5. 货币格式化

根据地区自动显示货币符号:

public static String currencyWithChosenLocalisation(double value, Locale locale) {
    NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
    return nf.format(value);
}

多地区示例:

double value = 23_500;
assertThat(currencyWithChosenLocalisation(value, new Locale("en", "US"))).isEqualTo("$23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("zh", "CN"))).isEqualTo("¥23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("pl", "PL"))).isEqualTo("23 500,00 zł");

4.6. 科学计数法格式化

超大/超小数字的科学计数法表示:

public static String formatScientificNotation(double value, Locale localisation) {
    return String.format(localisation, "%.3E", value);
}
Locale us = new Locale("en", "US");
assertThat(formatScientificNotation(3.14159, us)).isEqualTo("3.142E+00");
assertThat(formatScientificNotation(0.0123456, us)).isEqualTo("1.235E-02");
assertThat(formatScientificNotation(1111111, us)).isEqualTo("1.111E+06");

固定宽度格式化(不足补空格):

public static String formatScientificNotationWithMinChars(double value, Locale localisation) {
    return String.format(localisation, "%12.4E", value);
}
assertThat(formatScientificNotationWithMinChars(3.14159, us)).isEqualTo("  3.1416E+00");

5. 高级格式化技巧

DecimalFormat的本地化处理:

public static double withDecimalFormatLocal(double value) {
    DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.getDefault());
    return new Double(df.format(value));
}

自定义模式格式化:

public static double withDecimalFormatPattern(double value, int places) {
    DecimalFormat df2 = new DecimalFormat("#,###,###,##0.00");
    DecimalFormat df3 = new DecimalFormat("#,###,###,##0.000");
    if (places == 2)
        return new Double(df2.format(value));
    else if (places == 3)
        return new Double(df3.format(value));
    else
        throw new IllegalArgumentException();
}

assertThat(withDecimalFormatPattern(D, 2)).isEqualTo(4.24); 
assertThat(withDecimalFormatPattern(D, 3)).isEqualTo(4.235);

6. 总结

Java数字格式化方案对比:

场景 推荐方案 特点
基础格式化 String.format() 简单直接,适合快速实现
精确舍入 BigDecimal 金融级精度,可控性强
大整数显示 DecimalFormat 自动添加千位分隔符
货币/百分比 NumberFormat 本地化支持,自动适配地区
科学计数法 String.format() 灵活控制精度和宽度

💡 最佳实践

  • 金融计算优先使用BigDecimal
  • 需要本地化时选择NumberFormat
  • 简单场景用String.format()快速实现

所有示例代码已上传至GitHub仓库,欢迎参考实践。


原始标题:Number Formatting in Java | Baeldung