How to nicely format floating numbers to string without unnecessary decimal 0's
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()
orSystem.out.println
orFloatingDecimal.toJavaFormatString
uses scientific notations if double is less than 10^-3 or greater than or equal to 10^7double 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 doubledouble 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 ofDecimalFormat.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);
}
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, 2022Comments
-
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 about 15 yearsIf you are going to down vote, you need to at least leave a comment
-
jjnguy about 15 yearsI 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 about 14 yearsThat'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 over 13 yearsI voted up because I am having the same problem, and nobody here seems to understand the problem.
-
Steve Pomeroy about 13 yearsDownvoted as the DecimalFormat mentioned in Tom's post is exactly what you were looking for. It strips zeros quite effectively.
-
Zulaxia about 13 yearsDown 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 about 13 yearsDown 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 over 12 yearsTo 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 over 12 yearsThe 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 over 12 yearsThanks, 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 over 12 yearsThis 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 over 12 yearsI think you can handle the trailing zeroes correctly by using g instead of f.
-
Fletch over 12 yearsThis 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 over 12 yearsOk, then how would you be able to achieve my objective with the DecimalFormat?
-
Pyrolistical almost 12 yearsBy 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 almost 12 yearsBe careful, your solution will convert 1000 into 1, which is wrong.
-
Aleks G almost 12 yearsDecimalFormat with format of "#.##" will not keep extra 0 if they are not needed:
System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));
will print1
-
sethu almost 12 yearsthats my point. What if you want the 0.0005 displayed if there is any. You will be rounding it 2 decimal digits.
-
Aleks G almost 12 yearsThe OP is asking how to print integer values stored in doubles :)
-
Pyrolistical over 11 yearsIf you know the precision, then use a BigDecimal. See docs.oracle.com/javase/1.5.0/docs/api/java/math/…
-
autra about 11 yearsUpvoted 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 almost 11 yearsyou 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 almost 11 yearsformatFloatToString 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 almost 11 yearssometimes, 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 almost 11 yearsYou 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 almost 11 yearsI 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 almost 11 yearsI 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 almost 11 yearsjust 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 over 10 yearsyou did not replace the POINT, for example, "100.0" will be convert to "100."
-
Martin Klosi over 10 yearsif(f == (int)f) takes care of that.
-
Dawood ibn Kareem over 10 yearsFails on f = 9999999999.00
-
jlh about 10 yearsAgreed, this is a bad answer, do not use it. It fails to work with a
double
larger than the maximumint
value. Even withlong
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 about 10 yearsThe 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 thatint
was acceptable (unsigned not actually existing in Java, and no exemplar was problematic), but changingint
tolong
is a trivial fix if the situation is different. -
JasonD about 10 yearsWhere in the question does it say it shouldn't do that?
-
hamish almost 10 yearsI 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 over 9 yearsFYI, I changed "int" to "long", to cover a larger range of values, as suggested in the comments above.
-
kap about 9 yearsThis does not work for integers, e.g. "2" becomes "2."
-
JBE about 9 yearsThanks, I've fixed the answer by using the pattern
0
instead of#.
-
18446744073709551615 about 9 yearsDepending on the current locale, you may get the number formatted with spaces and commas inside.
-
Pyrolistical about 9 yearsyou should add this as a comment to the accepted answer
-
Terry Jan Reedy about 9 yearsComments 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 over 8 yearsSomebody 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 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. over 8 yearsI prefer this answer by which we don't need to do type conversion.
-
Andreas over 8 years
String.format("%s",d)
??? Talk about unnecessary overhead. UseDouble.toString(d)
. Same for the other:Long.toString((long)d)
. -
Maarten Bodewes over 8 yearsYou 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 equalsDecimalFormat.DOUBLE_FRACTION_DIGITS
. Why not just use the constant??? -
JBE over 8 yearsBecause this attribute is not public ... it is "package friendly"
-
Pyrolistical over 8 yearsjavascript made the same choice by the way :)
-
Spotted over 8 years@Pyrolistical Can you explain a bit more your statement ? It's not quite clear for me... :)
-
northernman over 8 yearsGood 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 about 8 yearsThe problem is that
%s
doesn't work with Locales. In German, we use a "," instead of a "." in decimal numbers. WhileString.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 about 8 yearsThanks! 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 almost 8 yearsI don't understand. If you said the formatting didn't matter, why did you spend the time to change it back?
-
android developer almost 8 years@OrhanC1 If it's not wrong in any way, it can stay with what I wrote.
-
OrhanC1 almost 8 yearsI agree. But because you changed it back, it sounds like it matters. You actively put the effort in. That's all.
-
TWiStErRob almost 8 yearsHmm, this doesn't take locales into account, but neither does JasonD's.
-
NurShomik over 7 yearsThanks! 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 alldouble
number prints. -
MC Emperor almost 7 yearsOr just
return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);
. -
blurfus almost 7 yearswhile 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 about 6 yearsString value = (f == (long) f) ? String.valueOf((long) f) : String.valueOf(f);
-
simonzhu about 6 yearsCame 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 over 5 yearsfail when 23.00123 ==> 23.00
-
JJ Brown about 5 yearsRepeating what Felix Edelmann said elsewhere: this will create a Locale-independent string, which may not always be appropriate for the user.
-
keisar almost 5 yearsfair 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 almost 5 yearsI would add
setMaximumFractionDigits(2)
andsetGroupingUsed(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 almost 5 yearsThanks, worked for me. This answer would have been accepted if it was me who posted the question. Great explaination
-
Neph over 4 yearsShort explanation:
"%s"
basically callsd.toString()
but it doesn't work withint
or ifd==null
! -
Alex78191 about 4 yearsWhat about 1.23450000?
-
Ahmed Mihoub about 4 years1.23450000 => 1.23
-
BekaBot over 3 yearsthe only solution that satisfied me
-
user924 over 3 yearsthis is not answer to that question. it always rounds to one number after a dot. Such a bad answer and offtopic
-
user924 over 3 yearswhat 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 over 3 yearsyou wrong answer doesn't return
232 0.18 1237875192 4.58 0 1.2345
-
user924 over 3 yearsyou wrong answer doesn't return
232 0.18 1237875192 4.58 0 1.2345
-
Peter Mortensen over 3 yearsAn explanation would be in order.
-
Peter Mortensen over 3 yearsDoes it actually work? What is 'n'? A floating point number of some kind? An integer?
-
yaboong about 3 yearsDecimalFormat is not thread-safe. You have to be careful when using it.
-
Yash Joshi almost 3 yearsAlthough 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 almost 3 yearsNice answer, it doesn't need an explanation as it do it by itself.
-
fop6316 almost 3 yearsExplanation added. I hope this will deserve at least 2 more up-votes ;-)
-
aligur over 2 yearsi really dont understand why this answer is voted up ): its not related with question.
-
Eugene over 2 yearsvery much like this.
-
Angad Cheema over 2 yearsfinally this worked. Thanks.
-
russellhoff almost 2 yearsThis 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"));