将浮点数格式化为最多N位小数

10

我希望将数字格式化为最多N个小数位。这里有一个类似而又广受欢迎的问题,但那并不是我要找的。

我想要的东西是这样的,假设我想要最大2位小数,那么就是:

1.00  -> "1"    (not "1.00")
1.20  -> "1.2"  (not "1.20")
1.23  -> "1.23"
1.234 -> "1.23"
1.235 -> "1.24"

与其他问题的区别在于,如果不需要,我不想要逗号后面的尾随零。

我想知道是否可以使用 String.format() 实现,而不是使用 Math.round() 或 DecimalFormat。上面提供了使用DecimalFormat的解决方案。

答案不需要给定N作为参数的变量。我只是选择N作为一个例子。


请查看您提到的问题的第二个答案。 - user3458
看一下使用BigDecimal(你链接的问题中的一个答案)。 - Krease
只需使用String.format()函数即可解决。 - PseudoAj
@Arkadiy - 第二个答案是DecimalFormat,而不是String.format(),对吗? - mac
1
你可以使用 DecimalFormat 和%s。 - user3458
显示剩余2条评论
5个回答

8

您可以使用DecimalFormat

引用文档:

您可以使用DecimalFormat类将十进制数格式化为与区域设置相关的字符串。此类允许您控制前导零、后导零、前缀和后缀、分组(千位)分隔符以及小数点的显示。

井号(#)表示数字,句点是小数点的占位符。

public void test(){
    DecimalFormat df = new DecimalFormat("#.##");
    System.out.println(df.format(1.00));
    System.out.println(df.format(1.20));
    System.out.println(df.format(1.23));
    System.out.println(df.format(1.234));
    System.out.println(df.format(1.235));
}

输出:

1
1.2
1.23
1.23
1.24

更新: 由于您更新了问题并希望使用String.format,在SO上搜索发现了这个thread,利用一个技巧加上正则表达式。因此,您可以使用类似以下的内容:

public static void main (String[] args) throws java.lang.Exception
{
    System.out.println(fmt(1.00));
    System.out.println(fmt(1.20));
    System.out.println(fmt(1.23));
    System.out.println(fmt(1.234));
    System.out.println(fmt(1.235));
}

public static String fmt(double d)
{
    if(d == (long) d)
        return String.format("%d",(long)d);
    else
        return String.format("%.2f",d).replaceAll("0*$", "");
}

输出结果为:
1
1.2
1.23
1.23
1.24

无论如何,我会使用 DecimalFormat

你会如何用“N”来表示小数位数? - 4castle
@4castle 只需确保在格式字符串中小数点后有 N# 符号。 - user207421
有没有办法用格式化程序进行编码,或者你必须使用 StringBuilder 在循环中构造模式? - 4castle
我在一开始提到的另一个问题中参考了DecimalFormat的解决方案。我想知道使用String.format()是否有相应的解决方案。 - mac
@4castle - 是的,那不太好。所以我们是说使用String.format()没有解决方案,只能使用DecimalFormat吗? - mac
显示剩余2条评论

3
您可以使用setMaximumFractionDigits(...)来控制DecimalFormat的格式,示例如下:
double d = 1.234567;
DecimalFormat df = new DecimalFormat();
for (int i = 2; i < 6; ++i) {
  df.setMaximumFractionDigits(i);
  System.out.println(df.format(d));
}

这对于与使用StringBuilder或类似方式生成格式不同的用例可能更好。


1
默认情况下,DecimalFormat 使用 RoundingMode.HALF_EVEN ,在对 5 进行四舍五入时行为不符合预期。要解决这个问题,可以使用 df.setRoundingMode(RoundingMode.HALF_UP); - 4castle

2
使用NumberFormat。
NumberFormat format = NumberFormat.getInstance();
format.setMaximumFractionDigits(2); // or N

System.out.println(format.format(1)); // -> 1
System.out.println(format.format(1.2)); // -> 1.2
System.out.println(format.format(1.23)); // -> 1.23
System.out.println(format.format(1.234)); // -> 1.23
System.out.println(format.format(1.235)); // -> 1.24

NumberFormat也可以与本地化一起使用,并且您可以更改舍入模式。

NumberFormat format = NumberFormat.getInstance(Locale.GERMANY);
format.setMaximumFractionDigits(2);
format.setRoundingMode(RoundingMode.CEILING);

System.out.println(format.format(1)); // -> 1
System.out.println(format.format(1.2)); // -> 1,2
System.out.println(format.format(1.23)); // -> 1,23
System.out.println(format.format(1.234)); // -> 1,24
System.out.println(format.format(1.235)); // -> 1,24

1
你的第二个代码块中有一个小错误,formatter 应该改为 format - Scratte

0
如果您仍然想使用String.format()来解决问题,这里有另一种解决方案:
Java代码:ParseN.java
public class ParseN{

     public static void main(String []args){
        System.out.println(parseN(1.00));
        System.out.println(parseN(1.20));
        System.out.println(parseN(1.23));
        System.out.println(parseN(1.234));
        System.out.println(parseN(1.235));
     }

     static String parseN(Double d)
     {
       String s = String.format("%.2f", d);
       s = s.indexOf(".") < 0 ? s : s.replaceAll("0*$", "").replaceAll("\\.$", "");
       return s;
     }
}

输出:

1
1.2
1.23
1.23
1.24

希望它完全回答了您的问题。此外,请参考this

1
你可以合并这两个正则表达式成为 \\.?0*$。此外,在生成的字符串中总会有一个小数点,所以不需要使用 indexOf 的三元运算符了。如果将 N 放入格式化的字符串中,这可能是迄今为止最完整的答案。 - 4castle

0
可能是使用String.format()最简洁的方法是通过以下测试程序中显示的fmt表达式。
static String fmtN(double d) {
    String fmt = String.format("%.2f", d).replaceFirst("\\.?(\\.[1-9])?0+$", "$1");
    System.out.println(fmt);
    return fmt;
}

@Test
public void test() {
    assertThat(fmtN(1), is("1"));
    assertThat(fmtN(1.05), is("1.05"));
    assertThat(fmtN(1.2), is("1.2"));
    assertThat(fmtN(1.23), is("1.23"));
    assertThat(fmtN(1.234), is("1.23"));
    assertThat(fmtN(1.235), is("1.24"));
}

所有血腥细节

String.format()可用的格式化程序不容易指定“最多N个”小数位数(例如,只能指定“正好2个”,如.2),因此我们必须使用正则表达式来去除尾部的零。对于第一种情况(没有小数位数),这比看起来要困难一些,为了还要去除尾部的.

以下是使此方法有效的几个关键点:

  • ?使前面的组或模式可选
  • (...)界定了一个捕获组,可以使用其位置编号在替换字符串中重新引入;所以这里$1复制了捕获组(\.[1-9])
  • +表示前面的模式出现一次或多次
  • \.转义了.(小数点)字符,因此它被用作字面量,而不是其正则表达式的意义上的“任意字符”(在Java字符串中,反斜杠本身必须被转义,所以每个正则表达式的反斜杠都写成\\)。
Pfeeew... 所以捕获组(\.[1-9]),意思是一个.后面跟着任何非零数字,使用?后缀标记为可选;因此,整个正则表达式在没有非零小数位的情况下仍能匹配,就像测试字符串1.00一样。如果该组存在,我们可以使用$1进行复制,就像我们之前看到的那样。如果不存在(如1.00),我们希望将.(小数点)去掉,以便它被\.?捕获。尾部的零由0+$$代表候选字符串的末尾)捕获并丢弃。
最后,我使用了replaceFirst()而不是replaceAll()作为一个小的优化;一旦正则表达式完成了它的工作,我们就可以停止寻找更多的匹配。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接