9

I'd like to format a number to maximum N decimal places. There's another similar and popular question here, but that's not exactly what I'm looking for.

I'm looking for something like this, let's say I want max. 2 places, so this would be:

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"

The difference to the other question is that I don't want trailing zeros behind the comma if I don't need them.

I'd like to know whether this is doable with String.format(), not with Math.round(), or DecimalFormat. The other question shown above provides a solution with DecimalFormat.

The answer does not need to be variable given N as an argument. I just chose N as an example.

Community
  • 1
  • 1
mac
  • 2,672
  • 4
  • 31
  • 43

4 Answers4

8

You can use DecimalFormat.

Quoting the documentation:

You can use the DecimalFormat class to format decimal numbers into locale-specific strings. This class allows you to control the display of leading and trailing zeros, prefixes and suffixes, grouping (thousands) separators, and the decimal separator.

The pound sign (#) denotes a digit and the period is a placeholder for the decimal separator.

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

Output:

1
1.2
1.23
1.23
1.24

Update: since you updated the question and you wanted to use String.format, searching in SO found this thread and leverage a trick plus regex. So, you could use something like this:

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*$", "");
}

The output is:

1
1.2
1.23
1.23
1.24

Anyway, I would use DecimalFormat instead.

Community
  • 1
  • 1
Federico Piazza
  • 30,085
  • 15
  • 87
  • 123
  • How would you put the decimal places in terms of `N`? – 4castle May 13 '16 at 00:56
  • @4castle Just make sure there are *N* `#` symbols after the decimal point in the format string. – user207421 May 13 '16 at 01:29
  • Is there a way to encode that with the formatter, or would you have to construct the pattern in a loop with a `StringBuilder`? – 4castle May 13 '16 at 01:30
  • @4castle: you could put multiple ######## as you want. Just put 1 as OP example. If # is found then it is used, is not nothing happens. Different than using 00#.##00, which will populate remaining characters with zeros – Federico Piazza May 13 '16 at 03:05
  • the other question I referenced from the start had a solution with DecimalFormat. I wanted to know what the solution is with String.format() assuming there is one. – mac May 13 '16 at 03:32
  • @mac There is a solution with `String.format()`, but it isn't the best solution because it requires string manipulation afterwards. – 4castle May 13 '16 at 03:34
  • @4castle - Yeah, that's not great. So are we saying that there's no solution with String.format(), only with DecimalFormat? – mac May 13 '16 at 03:35
3

You can also control the formatting of DecimalFormat using setMaximumFractionDigits(...) like so:

double d = 1.234567;
DecimalFormat df = new DecimalFormat();
for (int i = 2; i < 6; ++i) {
  df.setMaximumFractionDigits(i);
  System.out.println(df.format(d));
}

This might be better for your use case than generating a format using StringBuilder or similar.

clstrfsck
  • 14,715
  • 4
  • 44
  • 59
  • 1
    By default, `DecimalFormat` uses [`RoundingMode.HALF_EVEN`](https://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html#HALF_EVEN) which behaves unexpectedly when rounding a `5`. To fix that, do `df.setRoundingMode(RoundingMode.HALF_UP);` – 4castle May 13 '16 at 03:34
2

Use 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 also works with Locales and you can change the rounding mode.

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
0

Another solution if you still want to do it with String.format(), here is the solution:

Java Code: 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;
     }
}

output:

1
1.2
1.23
1.23
1.24

Hope it fully answers your question. Also, refer this.

Community
  • 1
  • 1
PseudoAj
  • 5,234
  • 2
  • 17
  • 37
  • You can consolidate the 2 regex to be `\\.?0*$`. Also, there will always be a decimal point in the resulting string, so the `indexOf` ternary isn't needed. If you put `N` into the format string, this may be the most complete answer yet. – 4castle May 13 '16 at 01:24