How to nicely format floating numbers to string without unnecessary decimal 0's

748,212

Solution 1

If the idea is to print integers stored as doubles as if they are integers, and otherwise print the doubles with the minimum necessary precision:

public static String fmt(double d)
{
    if(d == (long) d)
        return String.format("%d",(long)d);
    else
        return String.format("%s",d);
}

Produces:

232
0.18
1237875192
4.58
0
1.2345

And does not rely on string manipulation.

Solution 2

String.format("%.2f", value);

Solution 3

In short:

If you want to get rid of trailing zeros and locale problems, then you should use:

double myValue = 0.00000021d;

DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
df.setMaximumFractionDigits(340); //340 = DecimalFormat.DOUBLE_FRACTION_DIGITS

System.out.println(df.format(myValue)); //output: 0.00000021

Explanation:

Why other answers did not suit me:

  • Double.toString() or System.out.println or FloatingDecimal.toJavaFormatString uses scientific notations if double is less than 10^-3 or greater than or equal to 10^7

     double myValue = 0.00000021d;
     String.format("%s", myvalue); //output: 2.1E-7
    
  • by using %f, the default decimal precision is 6, otherwise you can hardcode it, but it results in extra zeros added if you have fewer decimals. Example:

     double myValue = 0.00000021d;
     String.format("%.12f", myvalue); // Output: 0.000000210000
    
  • by using setMaximumFractionDigits(0); or %.0f you remove any decimal precision, which is fine for integers/longs but not for double

     double myValue = 0.00000021d;
     System.out.println(String.format("%.0f", myvalue)); // Output: 0
     DecimalFormat df = new DecimalFormat("0");
     System.out.println(df.format(myValue)); // Output: 0
    
  • by using DecimalFormat, you are local dependent. In the French locale, the decimal separator is a comma, not a point:

     double myValue = 0.00000021d;
     DecimalFormat df = new DecimalFormat("0");
     df.setMaximumFractionDigits(340);
     System.out.println(df.format(myvalue)); // Output: 0,00000021
    

    Using the ENGLISH locale makes sure you get a point for decimal separator, wherever your program will run.

Why using 340 then for setMaximumFractionDigits?

Two reasons:

  • setMaximumFractionDigits accepts an integer, but its implementation has a maximum digits allowed of DecimalFormat.DOUBLE_FRACTION_DIGITS which equals 340
  • Double.MIN_VALUE = 4.9E-324 so with 340 digits you are sure not to round your double and lose precision

Solution 4

Use:

if (d % 1.0 != 0)
    return String.format("%s", d);
else
    return String.format("%.0f", d);

This should work with the extreme values supported by Double. It yields:

0.12
12
12.144252
0

Solution 5

On my machine, the following function is roughly 7 times faster than the function provided by JasonD's answer, since it avoids String.format:

public static String prettyPrint(double d) {
  int i = (int) d;
  return d == i ? String.valueOf(i) : String.valueOf(d);
}
Share:
748,212
Pyrolistical
Author by

Pyrolistical

Premature optimization is the root of all evil If you are not trying to learn all the time, then you are not a great developer User input is sacred

Updated on July 08, 2022

Comments

  • Pyrolistical
    Pyrolistical almost 2 years

    A 64-bit double can represent integer +/- 253 exactly.

    Given this fact, I choose to use a double type as a single type for all my types, since my largest integer is an unsigned 32-bit number.

    But now I have to print these pseudo integers, but the problem is they are also mixed in with actual doubles.

    So how do I print these doubles nicely in Java?

    I have tried String.format("%f", value), which is close, except I get a lot of trailing zeros for small values.

    Here's an example output of of %f

    232.00000000
    0.18000000000
    1237875192.0
    4.5800000000
    0.00000000
    1.23450000
    

    What I want is:

    232
    0.18
    1237875192
    4.58
    0
    1.2345
    

    Sure I can write a function to trim those zeros, but that's lot of performance loss due to string manipulation. Can I do better with other format code?


    The answers by Tom E. and Jeremy S. are unacceptable as they both arbitrarily rounds to two decimal places. Please understand the problem before answering.


    Please note that String.format(format, args...) is locale-dependent (see answers below).

  • Pyrolistical
    Pyrolistical about 15 years
    If you are going to down vote, you need to at least leave a comment
  • jjnguy
    jjnguy about 15 years
    I downvoted because your solution is not the best way to go. Have a look at String.format. You need to use the correct format type, float in this instance. Look at my above answer.
  • Emre Yazici
    Emre Yazici about 14 years
    That's correct but always prints trailing zeros even if there is no fractional part. String.format("%.2f, 1.0005) prints 1.00 and not 1. Is there any format specifier for not to print fractional part if it does not exist?
  • Francesco Mangia
    Francesco Mangia over 13 years
    I voted up because I am having the same problem, and nobody here seems to understand the problem.
  • Steve Pomeroy
    Steve Pomeroy about 13 years
    Downvoted as the DecimalFormat mentioned in Tom's post is exactly what you were looking for. It strips zeros quite effectively.
  • Zulaxia
    Zulaxia about 13 years
    Down voted since the question is asking to strip all trailing zeros and this answer will always leave two floating points regardless of being zero.
  • Zulaxia
    Zulaxia about 13 years
    Down voted for the same reason as Steve Pomeroy. The DecimalFormat answer is the best way to do this and should really be the accepted answer here.
  • Rehno Lindeque
    Rehno Lindeque over 12 years
    To the above, maybe he wants to trim the zeros WITHOUT rounding? P.S. @Pyrolistical, surely you can just use number.replaceAll(".?0*$", ""); (after contains(".") of course)
  • Siken
    Siken over 12 years
    The DecimalFormat was a nice trick -- although I ended up using this one for my situation (game level timer) as the trailing zeros looked better.
  • Pyrolistical
    Pyrolistical over 12 years
    Thanks, my regex was rather pathetic when I first posted that. And Rehno nailed it on the head Tom's code rounds to 2 decimals places.
  • bakoyaro
    bakoyaro over 12 years
    This is a good solution to the question, if they were only interested in trailing zeroes being dropped, how would you change your code to also trim a trailing decimal point? i.e. "1."
  • Peter Ajtai
    Peter Ajtai over 12 years
    I think you can handle the trailing zeroes correctly by using g instead of f.
  • Fletch
    Fletch over 12 years
    This solution is not localisable, which is one example why it is the wrong way to do it. If one day your application runs under a European locale, which uses commas as the decimal separator, it won't work. Whereas DecimalFormat automatically works under any locale. You might not plan to ever run in Europe but there may be other gotchas you haven't considered - using the inbuilt methods gives you the best chance to avoid them.
  • Pyrolistical
    Pyrolistical over 12 years
    Ok, then how would you be able to achieve my objective with the DecimalFormat?
  • Pyrolistical
    Pyrolistical almost 12 years
    By the way if I had localisation concerns I can add more logic. The most voted answer by @Tom does not fill the requirements. Tom's code is arbitrarily round to 2 decimal places.
  • Aleks G
    Aleks G almost 12 years
    Be careful, your solution will convert 1000 into 1, which is wrong.
  • Aleks G
    Aleks G almost 12 years
    DecimalFormat with format of "#.##" will not keep extra 0 if they are not needed: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005)); will print 1
  • sethu
    sethu almost 12 years
    thats my point. What if you want the 0.0005 displayed if there is any. You will be rounding it 2 decimal digits.
  • Aleks G
    Aleks G almost 12 years
    The OP is asking how to print integer values stored in doubles :)
  • Pyrolistical
    Pyrolistical over 11 years
    If you know the precision, then use a BigDecimal. See docs.oracle.com/javase/1.5.0/docs/api/java/math/…
  • autra
    autra about 11 years
    Upvoted for understanding the problem well. But the regex should be "\\.?0*$". Otherwise, for 34.23298424000 it will give back 34.2329842 and not 34.23298424.
  • android developer
    android developer almost 11 years
    you should instead just search for the first non-zero-digit from the right and then use the subString ( and also verify that the string contains "." of course). this way, you won't come into creating so many temporary strings on the way.
  • Pyrolistical
    Pyrolistical almost 11 years
    formatFloatToString can be vastly simplified with docs.oracle.com/javase/6/docs/api/java/lang/… and docs.oracle.com/javase/6/docs/api/java/lang/…, int)
  • android developer
    android developer almost 11 years
    sometimes, when you make things more simple, the code behind is more complex and less optimized... but yes, you can use plenty of built in API functions...
  • Pyrolistical
    Pyrolistical almost 11 years
    You should start with simple and once you have determined you have a performance problem, then and only then should you optimize. Code is for the human to read again and again. Making it run fast is secondary. By not using the standard API whenever possible you are more likely to introduce bugs and only makes it more difficult to change in the future.
  • android developer
    android developer almost 11 years
    I disagree. Base code should run as fast as possible, as many others might use it. Performance is a huge concern and this is something people are taught about a lot at the university. You can't predict how much you function will be used. Only the most higher functions can be exactly as you say. As an example, think what would a delay of 1 second be for each time you use internet connection. would you agree to those who made the class if they told you that their code is slow because it's a simple code?
  • Pyrolistical
    Pyrolistical almost 11 years
    I would argue code you write like that is NOT going to be any faster. The JVM is very smart and you don't actually know how fast or slow something is until you profile it. Performance problems can be detected and fixed when it becomes a problem. You should not prematurely optimize for it. Write code for people to read, not for how you imagine the machine is going to run it. Once it becomes a performance problem, rewrite code with a profiler.
  • android developer
    android developer almost 11 years
    just tried to make a point. if you wish using a short code, you can look at the first example, which works fine, and might even work better.
  • VinceStyling
    VinceStyling over 10 years
    you did not replace the POINT, for example, "100.0" will be convert to "100."
  • Martin Klosi
    Martin Klosi over 10 years
    if(f == (int)f) takes care of that.
  • Dawood ibn Kareem
    Dawood ibn Kareem over 10 years
    Fails on f = 9999999999.00
  • jlh
    jlh about 10 years
    Agreed, this is a bad answer, do not use it. It fails to work with a double larger than the maximum int value. Even with long it would still fail for huge numbers. Further it will return a String in exponential form, e.g. "1.0E10", for large values, which is probably not what the asker wants. Use %f instead of %s in the second format string to fix that.
  • JasonD
    JasonD about 10 years
    The OP stated explicitly that they did not want the output formatted using %f. The answer is specific to the situation described, and the desired output. The OP suggested their maximum value was a 32-bit unsigned int, which I took to mean that int was acceptable (unsigned not actually existing in Java, and no exemplar was problematic), but changing int to long is a trivial fix if the situation is different.
  • JasonD
    JasonD about 10 years
    Where in the question does it say it shouldn't do that?
  • hamish
    hamish almost 10 years
    I used this solution in a production system with "%.5f", and it is really really bad, do not use it... because it printed this: 5.12E-4 instead of 0.000512
  • ToolmakerSteve
    ToolmakerSteve over 9 years
    FYI, I changed "int" to "long", to cover a larger range of values, as suggested in the comments above.
  • kap
    kap about 9 years
    This does not work for integers, e.g. "2" becomes "2."
  • JBE
    JBE about 9 years
    Thanks, I've fixed the answer by using the pattern 0 instead of #.
  • 18446744073709551615
    18446744073709551615 about 9 years
    Depending on the current locale, you may get the number formatted with spaces and commas inside.
  • Pyrolistical
    Pyrolistical about 9 years
    you should add this as a comment to the accepted answer
  • Terry Jan Reedy
    Terry Jan Reedy about 9 years
    Comments do not allow the length or format of this addendum. Since it adds possibly useful information, I think it should be allowed as is rather than deleted.
  • Steve Vinoski
    Steve Vinoski over 8 years
    Somebody else edited the answer to improve the code formatting. I was reviewing several dozen edits for approval and was going to approve their edit here, but the edits were inconsistent so I fixed them. I also improved the grammar of the text snippets.
  • android developer
    android developer over 8 years
    @SteveVinoski Grammar of English - ok (English isn't my main langauge, and when I don't have enough time, I can't review what I write), but the code formatting doesn't need to get improved. It's a matter of taste, and what I use is always one of the standard code formatting. When the "{" align with the rest of the code block, it's called "Whitesmiths " : en.wikipedia.org/wiki/Indent_style#Whitesmiths_style . Any developer should be able to read this code formatting, just like any other code formatting. Besides, you can always copy and format via the IDE.
  • Jeff T.
    Jeff T. over 8 years
    I prefer this answer by which we don't need to do type conversion.
  • Andreas
    Andreas over 8 years
    String.format("%s",d)??? Talk about unnecessary overhead. Use Double.toString(d). Same for the other: Long.toString((long)d).
  • Maarten Bodewes
    Maarten Bodewes over 8 years
    You are not using the constant DecimalFormat.DOUBLE_FRACTION_DIGITS but you are using the value 340, which you then provide a comment for to show that it equals DecimalFormat.DOUBLE_FRACTION_DIGITS. Why not just use the constant???
  • JBE
    JBE over 8 years
    Because this attribute is not public ... it is "package friendly"
  • Pyrolistical
    Pyrolistical over 8 years
    javascript made the same choice by the way :)
  • Spotted
    Spotted over 8 years
    @Pyrolistical Can you explain a bit more your statement ? It's not quite clear for me... :)
  • northernman
    northernman over 8 years
    Good solution if you know that the "d" value is always in a valid range. Agree with Andreas about not using String.format() though.
  • Felix Edelmann
    Felix Edelmann about 8 years
    The problem is that %s doesn't work with Locales. In German, we use a "," instead of a "." in decimal numbers. While String.format(Locale.GERMAN, "%f", 1.5) returns "1,500000", String.format(Locale.GERMAN, "%s", 1.5) returns "1.5" – with a ".", which is false in German language. Is there a locale-dependent version of "%s" as well?
  • Felix Edelmann
    Felix Edelmann about 8 years
    Thanks! In fact, this answer is the only one that really matches all requirements mentioned in the question – it doesn't show unnecessary zeros, doesn't round the numbers and is locale-dependant. Great!
  • OrhanC1
    OrhanC1 almost 8 years
    I don't understand. If you said the formatting didn't matter, why did you spend the time to change it back?
  • android developer
    android developer almost 8 years
    @OrhanC1 If it's not wrong in any way, it can stay with what I wrote.
  • OrhanC1
    OrhanC1 almost 8 years
    I agree. But because you changed it back, it sounds like it matters. You actively put the effort in. That's all.
  • TWiStErRob
    TWiStErRob almost 8 years
    Hmm, this doesn't take locales into account, but neither does JasonD's.
  • NurShomik
    NurShomik over 7 years
    Thanks! My requirement was to remove the trailing zeroes without loosing precision. This is so far the best answer I've found online. Initializing df just once in the class did the trick for all double number prints.
  • MC Emperor
    MC Emperor almost 7 years
    Or just return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
  • blurfus
    blurfus almost 7 years
    while this may provide a solution to the question, it is good practice to add a brief explanation for the community to benefit (and learn) from the answer
  • Lukas Hanacek
    Lukas Hanacek about 6 years
    String value = (f == (long) f) ? String.valueOf((long) f) : String.valueOf(f);
  • simonzhu
    simonzhu about 6 years
    Came across this problem as I was working on a contest problem: I came across this solution as well, but it didn't seem to solve the problem for me as for some test cases, it would format the solution into scientific notation, which is not ideal.
  • aswzen
    aswzen over 5 years
    fail when 23.00123 ==> 23.00
  • JJ Brown
    JJ Brown about 5 years
    Repeating what Felix Edelmann said elsewhere: this will create a Locale-independent string, which may not always be appropriate for the user.
  • keisar
    keisar almost 5 years
    fair point, for my use case this was not an issue, I'm not entirely sure right now but I think one could use String.format (with the wanted Locale) instead of valueOf
  • acrespo
    acrespo almost 5 years
    I would add setMaximumFractionDigits(2) and setGroupingUsed(false) (OP's doesn't mention it but from example it seems that its required). Also, a small test case doesn't hurt since its trivial in this case. Still, since I think it its the simplest solution, an upvote is an upvote :)
  • Demon App Programmer
    Demon App Programmer almost 5 years
    Thanks, worked for me. This answer would have been accepted if it was me who posted the question. Great explaination
  • Neph
    Neph over 4 years
    Short explanation: "%s" basically calls d.toString() but it doesn't work with int or if d==null!
  • Alex78191
    Alex78191 about 4 years
    What about 1.23450000?
  • Ahmed Mihoub
    Ahmed Mihoub about 4 years
    1.23450000 => 1.23
  • BekaBot
    BekaBot over 3 years
    the only solution that satisfied me
  • user924
    user924 over 3 years
    this is not answer to that question. it always rounds to one number after a dot. Such a bad answer and offtopic
  • user924
    user924 over 3 years
    what are you doing? it always rounds to 1 digit after the dot, it's not answer to the question. why some people cannot read?
  • user924
    user924 over 3 years
    you wrong answer doesn't return 232 0.18 1237875192 4.58 0 1.2345
  • user924
    user924 over 3 years
    you wrong answer doesn't return 232 0.18 1237875192 4.58 0 1.2345
  • Peter Mortensen
    Peter Mortensen over 3 years
    An explanation would be in order.
  • Peter Mortensen
    Peter Mortensen over 3 years
    Does it actually work? What is 'n'? A floating point number of some kind? An integer?
  • yaboong
    yaboong about 3 years
    DecimalFormat is not thread-safe. You have to be careful when using it.
  • Yash Joshi
    Yash Joshi almost 3 years
    Although this is exactly opposite to what the OP is tryna ask but I'm grateful this existed here, I was looking for just this!
  • Fabrizio Valencia
    Fabrizio Valencia almost 3 years
    Nice answer, it doesn't need an explanation as it do it by itself.
  • fop6316
    fop6316 almost 3 years
    Explanation added. I hope this will deserve at least 2 more up-votes ;-)
  • aligur
    aligur over 2 years
    i really dont understand why this answer is voted up ): its not related with question.
  • Eugene
    Eugene over 2 years
    very much like this.
  • Angad Cheema
    Angad Cheema over 2 years
    finally this worked. Thanks.
  • russellhoff
    russellhoff almost 2 years
    This is the read answer. I'd point out that you can get an especific Locale based on its country code, such as NumberFormat.getInstance(Locale.forLanguageTag("ES"));