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

29

617

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).

Valedictorian answered 31/3, 2009 at 22:54 Comment(5)
If all you want are integers, why not use a long? You get more bang at 2^63-1, no awkward formatting, and better performance.Natividadnativism
Because some values are actually doublesValedictorian
Some cases where this problem occured was a bug fixed in JDK 7: #7565025Valedictorian
System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);Zoospore
This is the correct answerBacking
C
453

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.

Cytolysis answered 2/1, 2013 at 17:52 Comment(10)
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.Vachell
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.Cytolysis
Where in the question does it say it shouldn't do that?Cytolysis
FYI, I changed "int" to "long", to cover a larger range of values, as suggested in the comments above.Caroche
Depending on the current locale, you may get the number formatted with spaces and commas inside.Imaret
String.format("%s",d)??? Talk about unnecessary overhead. Use Double.toString(d). Same for the other: Long.toString((long)d).Packet
Good solution if you know that the "d" value is always in a valid range. Agree with Andreas about not using String.format() though.Tequilater
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?Emmett
String value = (f == (long) f) ? String.valueOf((long) f) : String.valueOf(f);Empoverish
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.Sapanwood
G
234
String.format("%.2f", value);
Granger answered 14/8, 2009 at 4:52 Comment(7)
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?Bowls
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.Gap
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.Willmert
I think you can handle the trailing zeroes correctly by using g instead of f.Culbert
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.000512Material
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!Argybargy
i really dont understand why this answer is voted up ): its not related with question.Bank
F
140

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
Finedraw answered 14/8, 2014 at 12:35 Comment(7)
This does not work for integers, e.g. "2" becomes "2."Warmedover
Thanks, I've fixed the answer by using the pattern 0 instead of #.Finedraw
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???Ullund
Because this attribute is not public ... it is "package friendly"Finedraw
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!Emmett
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.Bract
Thanks, worked for me. This answer would have been accepted if it was me who posted the question. Great explainationHutt
P
49

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
Pishogue answered 1/10, 2014 at 0:53 Comment(3)
I prefer this answer by which we don't need to do type conversion.Ctenophore
Short explanation: "%s" basically calls d.toString() but it doesn't work with int or if d==null!Viminal
very much like this.Settera
C
35

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);
}
Chaoan answered 9/2, 2014 at 22:11 Comment(1)
Hmm, this doesn't take locales into account, but neither does JasonD's.Doublereed
T
25

My two cents:

if(n % 1 == 0) {
    return String.format(Locale.US, "%.0f", n));
} else {
    return String.format(Locale.US, "%.1f", n));
}
Tonometer answered 5/8, 2016 at 9:36 Comment(5)
Or just return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.Bohner
fail when 23.00123 ==> 23.00Jewelry
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?Sea
you wrong answer doesn't return 232 0.18 1237875192 4.58 0 1.2345Sea
Does it actually work? What is 'n'? A floating point number of some kind? An integer?Undertake
A
21
if (d == Math.floor(d)) {
    return String.format("%.0f", d); //Format is: 0 places after decimal point
} else {
    return Double.toString(d);
}

More info: https://docs.oracle.com/javase/tutorial/java/data/numberformat.html

Aspirant answered 29/7, 2016 at 10:33 Comment(3)
An explanation would be in order.Undertake
Nice answer, it doesn't need an explanation as it do it by itself.Waistband
Explanation added. I hope this will deserve at least 2 more up-votes ;-)Aspirant
T
21
float price = 4.30;
DecimalFormat format = new DecimalFormat("0.##"); // Choose the number of decimal places to work with in case they are different than zero and zero value will be removed
format.setRoundingMode(RoundingMode.DOWN); // Choose your Rounding Mode
System.out.println(format.format(price));

This is the result of some tests:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4
Thermochemistry answered 9/12, 2019 at 14:22 Comment(5)
What about 1.23450000?Moonshiner
1.23450000 => 1.23Thermochemistry
the only solution that satisfied meFebri
DecimalFormat is not thread-safe. You have to be careful when using it.Freshet
finally this worked. Thanks.Widower
V
17

Naw, never mind. The performance loss due to string manipulation is zero.

And here's the code to trim the end after %f:

private static String trimTrailingZeros(String number) {
    if(!number.contains(".")) {
        return number;
    }

    return number.replaceAll("\\.?0*$", "");
}
Valedictorian answered 31/3, 2009 at 23:3 Comment(11)
If you are going to down vote, you need to at least leave a commentValedictorian
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.Abductor
I voted up because I am having the same problem, and nobody here seems to understand the problem.Helper
Downvoted as the DecimalFormat mentioned in Tom's post is exactly what you were looking for. It strips zeros quite effectively.Tinsel
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.Gap
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)Ducan
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.Valedictorian
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.Waters
Ok, then how would you be able to achieve my objective with the DecimalFormat?Valedictorian
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.Valedictorian
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.Pinole
G
11

This one will gets the job done nicely:

    public static String removeZero(double number) {
        DecimalFormat format = new DecimalFormat("#.###########");
        return format.format(number);
    }
Greaves answered 10/9, 2016 at 7:9 Comment(0)
C
11

Use a DecimalFormat and setMinimumFractionDigits(0).

Chowchow answered 10/10, 2017 at 19:59 Comment(1)
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 :)Decemvirate
I
6

Please note that String.format(format, args...) is locale-dependent because it formats using the user's default locale, that is, probably with commas and even spaces inside like 123 456,789 or 123,456.789, which may be not exactly what you expect.

You may prefer to use String.format((Locale)null, format, args...).

For example,

    double f = 123456.789d;
    System.out.println(String.format(Locale.FRANCE,"%f",f));
    System.out.println(String.format(Locale.GERMANY,"%f",f));
    System.out.println(String.format(Locale.US,"%f",f));

prints

123456,789000
123456,789000
123456.789000

and this is what will String.format(format, args...) do in different countries.

EDIT Ok, since there has been a discussion about formalities:

    res += stripFpZeroes(String.format((Locale) null, (nDigits!=0 ? "%."+nDigits+"f" : "%f"), value));
    ...

protected static String stripFpZeroes(String fpnumber) {
    int n = fpnumber.indexOf('.');
    if (n == -1) {
        return fpnumber;
    }
    if (n < 2) {
        n = 2;
    }
    String s = fpnumber;
    while (s.length() > n && s.endsWith("0")) {
        s = s.substring(0, s.length()-1);
    }
    return s;
}
Imaret answered 13/3, 2015 at 10:24 Comment(2)
you should add this as a comment to the accepted answerValedictorian
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.Lehmbruck
N
6
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 for minimum number of digits
  2. Renders # digits
Natiha answered 14/7, 2017 at 8:29 Comment(3)
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 answerLeffler
this is not answer to that question. it always rounds to one number after a dot. Such a bad answer and offtopicSea
you wrong answer doesn't return 232 0.18 1237875192 4.58 0 1.2345Sea
I
5

I made a DoubleFormatter to efficiently convert a great numbers of double values to a nice/presentable string:

double horribleNumber = 3598945.141658554548844;
DoubleFormatter df = new DoubleFormatter(4, 6); // 4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • If the integer part of V has more than MaxInteger => display V in scientific format (1.2345E+30). Otherwise, display in normal format (124.45678).
  • the MaxDecimal decide numbers of decimal digits (trim with bankers' rounding)

Here the code:

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;

/**
 * Convert a double to a beautiful String (US-local):
 *
 * double horribleNumber = 3598945.141658554548844;
 * DoubleFormatter df = new DoubleFormatter(4,6);
 * String beautyDisplay = df.format(horribleNumber);
 * String beautyLabel = df.formatHtml(horribleNumber);
 *
 * Manipulate 3 instances of NumberFormat to efficiently format a great number of double values.
 * (avoid to create an object NumberFormat each call of format()).
 *
 * 3 instances of NumberFormat will be reused to format a value v:
 *
 * if v < EXP_DOWN, uses nfBelow
 * if EXP_DOWN <= v <= EXP_UP, uses nfNormal
 * if EXP_UP < v, uses nfAbove
 *
 * nfBelow, nfNormal and nfAbove will be generated base on the precision_ parameter.
 *
 * @author: DUONG Phu-Hiep
 */
public class DoubleFormatter
{
    private static final double EXP_DOWN = 1.e-3;
    private double EXP_UP; // always = 10^maxInteger
    private int maxInteger_;
    private int maxFraction_;
    private NumberFormat nfBelow_;
    private NumberFormat nfNormal_;
    private NumberFormat nfAbove_;

    private enum NumberFormatKind {Below, Normal, Above}

    public DoubleFormatter(int maxInteger, int maxFraction){
        setPrecision(maxInteger, maxFraction);
    }

    public void setPrecision(int maxInteger, int maxFraction){
        Preconditions.checkArgument(maxFraction>=0);
        Preconditions.checkArgument(maxInteger>0 && maxInteger<17);

        if (maxFraction == maxFraction_ && maxInteger_ == maxInteger) {
            return;
        }

        maxFraction_ = maxFraction;
        maxInteger_ = maxInteger;
        EXP_UP =  Math.pow(10, maxInteger);
        nfBelow_ = createNumberFormat(NumberFormatKind.Below);
        nfNormal_ = createNumberFormat(NumberFormatKind.Normal);
        nfAbove_ = createNumberFormat(NumberFormatKind.Above);
    }

    private NumberFormat createNumberFormat(NumberFormatKind kind) {

        // If you do not use the Guava library, replace it with createSharp(precision);
        final String sharpByPrecision = Strings.repeat("#", maxFraction_);

        NumberFormat f = NumberFormat.getInstance(Locale.US);

        // Apply bankers' rounding:  this is the rounding mode that
        // statistically minimizes cumulative error when applied
        // repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            // Set group separator to space instead of comma

            //dfs.setGroupingSeparator(' ');

            // Set Exponent symbol to minus 'e' instead of 'E'
            if (kind == NumberFormatKind.Above) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }

            df.setDecimalFormatSymbols(dfs);

            // Use exponent format if v is outside of [EXP_DOWN,EXP_UP]

            if (kind == NumberFormatKind.Normal) {
                if (maxFraction_ == 0) {
                    df.applyPattern("#,##0");
                } else {
                    df.applyPattern("#,##0."+sharpByPrecision);
                }
            } else {
                if (maxFraction_ == 0) {
                    df.applyPattern("0E0");
                } else {
                    df.applyPattern("0."+sharpByPrecision+"E0");
                }
            }
        }
        return f;
    }

    public String format(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        if (v==0) {
            return "0";
        }
        final double absv = Math.abs(v);

        if (absv<EXP_DOWN) {
            return nfBelow_.format(v);
        }

        if (absv>EXP_UP) {
            return nfAbove_.format(v);
        }

        return nfNormal_.format(v);
    }

    /**
     * Format and higlight the important part (integer part & exponent part)
     */
    public String formatHtml(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        return htmlize(format(v));
    }

    /**
     * This is the base alogrithm: create a instance of NumberFormat for the value, then format it. It should
     * not be used to format a great numbers of value
     *
     * We will never use this methode, it is here only to understanding the Algo principal:
     *
     * format v to string. precision_ is numbers of digits after decimal.
     * if EXP_DOWN <= abs(v) <= EXP_UP, display the normal format: 124.45678
     * otherwise display scientist format with: 1.2345e+30
     *
     * pre-condition: precision >= 1
     */
    @Deprecated
    public String formatInefficient(double v) {

        // If you do not use Guava library, replace with createSharp(precision);
        final String sharpByPrecision = Strings.repeat("#", maxFraction_);

        final double absv = Math.abs(v);

        NumberFormat f = NumberFormat.getInstance(Locale.US);

        // Apply bankers' rounding:  this is the rounding mode that
        // statistically minimizes cumulative error when applied
        // repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            // Set group separator to space instead of comma

            dfs.setGroupingSeparator(' ');

            // Set Exponent symbol to minus 'e' instead of 'E'

            if (absv>EXP_UP) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }
            df.setDecimalFormatSymbols(dfs);

            //use exponent format if v is out side of [EXP_DOWN,EXP_UP]

            if (absv<EXP_DOWN || absv>EXP_UP) {
                df.applyPattern("0."+sharpByPrecision+"E0");
            } else {
                df.applyPattern("#,##0."+sharpByPrecision);
            }
        }
        return f.format(v);
    }

    /**
     * Convert "3.1416e+12" to "<b>3</b>.1416e<b>+12</b>"
     * It is a html format of a number which highlight the integer and exponent part
     */
    private static String htmlize(String s) {
        StringBuilder resu = new StringBuilder("<b>");
        int p1 = s.indexOf('.');

        if (p1>0) {
            resu.append(s.substring(0, p1));
            resu.append("</b>");
        } else {
            p1 = 0;
        }

        int p2 = s.lastIndexOf('e');
        if (p2>0) {
            resu.append(s.substring(p1, p2));
            resu.append("<b>");
            resu.append(s.substring(p2, s.length()));
            resu.append("</b>");
        } else {
            resu.append(s.substring(p1, s.length()));
            if (p1==0){
                resu.append("</b>");
            }
        }
        return resu.toString();
    }
}

Note: I used two functions from the Guava library. If you don't use Guava, code it yourself:

/**
 * Equivalent to Strings.repeat("#", n) of the Guava library:
 */
private static String createSharp(int n) {
    StringBuilder sb = new StringBuilder();
    for (int i=0; i<n; i++) {
        sb.append('#');
    }
    return sb.toString();
}
Ic answered 26/11, 2012 at 20:55 Comment(1)
If you know the precision, then use a BigDecimal. See docs.oracle.com/javase/1.5.0/docs/api/java/math/…Valedictorian
S
4
String s = String.valueof("your int variable");
while (g.endsWith("0") && g.contains(".")) {
    g = g.substring(0, g.length() - 1);
    if (g.endsWith("."))
    {
        g = g.substring(0, g.length() - 1);
    }
}
Spun answered 11/1, 2013 at 6:33 Comment(1)
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.Trinomial
L
4

You said you choose to store your numbers with the double type. I think this could be the root of the problem, because it forces you to store integers into doubles (and therefore losing the initial information about the value's nature). What about storing your numbers in instances of the Number class (superclass of both Double and Integer) and rely on polymorphism to determine the correct format of each number?

I know it may not be acceptable to refactor a whole part of your code due to that, but it could produce the desired output without extra code/casting/parsing.

Example:

import java.util.ArrayList;
import java.util.List;

public class UseMixedNumbers {

    public static void main(String[] args) {
        List<Number> listNumbers = new ArrayList<Number>();

        listNumbers.add(232);
        listNumbers.add(0.18);
        listNumbers.add(1237875192);
        listNumbers.add(4.58);
        listNumbers.add(0);
        listNumbers.add(1.2345);

        for (Number number : listNumbers) {
            System.out.println(number);
        }
    }

}

Will produce the following output:

232
0.18
1237875192
4.58
0
1.2345
Lithuanian answered 23/4, 2015 at 6:59 Comment(2)
javascript made the same choice by the way :)Valedictorian
@Valedictorian Can you explain a bit more your statement ? It's not quite clear for me... :)Lithuanian
U
3

For Kotlin you can use an extension like:

fun Double.toPrettyString() =
    if(this - this.toLong() == 0.0)
        String.format("%d", this.toLong())
    else
        String.format("%s", this)
Unseal answered 5/12, 2019 at 10:14 Comment(0)
W
2

This is what I came up with:

  private static String format(final double dbl) {
    return dbl % 1 != 0 ? String.valueOf(dbl) : String.valueOf((int) dbl);
  }

It is a simple one-liner and only casts to int if it really needs to.

Whangee answered 11/4, 2018 at 17:45 Comment(2)
Repeating what Felix Edelmann said elsewhere: this will create a Locale-independent string, which may not always be appropriate for the user.Atcliffe
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 valueOfWhangee
E
2

Format price with grouping, rounding, and no unnecessary zeroes (in double).

Rules:

  1. No zeroes at the end (2.0000 = 2; 1.0100000 = 1.01)
  2. Two digits maximum after a point (2.010 = 2.01; 0.20 = 0.2)
  3. Rounding after the 2nd digit after a point (1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. Returns 0 (null/-0 = 0)
  5. Adds $ (= $56/-$56)
  6. Grouping (101101.02 = $101,101.02)

More examples:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

It is written in Kotlin as a fun extension of Double (because it is used in Android), but it can be converted to Java easily, because Java classes were used.

/**
 * 23.0 -> $23
 *
 * 23.1 -> $23.1
 *
 * 23.01 -> $23.01
 *
 * 23.99 -> $23.99
 *
 * 23.999 -> $24
 *
 * -0.0 -> $0
 *
 * -5.00 -> -$5
 *
 * -5.019 -> -$5.02
 */
fun Double?.formatUserAsSum(): String {
    return when {
        this == null || this == 0.0 -> "$0"
        this % 1 == 0.0 -> DecimalFormat("$#,##0;-$#,##0").format(this)
        else -> DecimalFormat("$#,##0.##;-$#,##0.##").format(this)
    }
}

How to use:

var yourDouble: Double? = -20.00
println(yourDouble.formatUserAsSum()) // will print -$20

yourDouble = null
println(yourDouble.formatUserAsSum()) // will print $0

About DecimalFormat: https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

Elatia answered 11/9, 2019 at 11:34 Comment(0)
S
2

A simple solution with locale in mind:

double d = 123.45;
NumberFormat numberFormat = NumberFormat.getInstance(Locale.GERMANY);
System.out.println(numberFormat.format(d));

Since comma is used as decimal separator in Germany, the above will print:

123,45

Scarce answered 6/9, 2021 at 15:45 Comment(1)
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"));Upi
S
1

Here's another answer that has an option to append decimal ONLY IF decimal was not zero.

   /**
     * Example: (isDecimalRequired = true)
     * d = 12345
     * returns 12,345.00
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * ==================================================
     * Example: (isDecimalRequired = false)
     * d = 12345
     * returns 12,345 (notice that there's no decimal since it's zero)
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * @param d float to format
     * @param zeroCount number decimal places
     * @param isDecimalRequired true if it will put decimal even zero,
     * false will remove the last decimal(s) if zero.
     */
    fun formatDecimal(d: Float? = 0f, zeroCount: Int, isDecimalRequired: Boolean = true): String {
        val zeros = StringBuilder()

        for (i in 0 until zeroCount) {
            zeros.append("0")
        }

        var pattern = "#,##0"

        if (zeros.isNotEmpty()) {
            pattern += ".$zeros"
        }

        val numberFormat = DecimalFormat(pattern)

        var formattedNumber = if (d != null) numberFormat.format(d) else "0"

        if (!isDecimalRequired) {
            for (i in formattedNumber.length downTo formattedNumber.length - zeroCount) {
                val number = formattedNumber[i - 1]

                if (number == '0' || number == '.') {
                    formattedNumber = formattedNumber.substring(0, formattedNumber.length - 1)
                } else {
                    break
                }
            }
        }

        return formattedNumber
    }
Sycophancy answered 14/5, 2020 at 15:7 Comment(0)
T
0

Here are two ways to achieve it. First, the shorter (and probably better) way:

public static String formatFloatToString(final float f)
{
  final int i = (int)f;
  if(f == i)
    return Integer.toString(i);
  return Float.toString(f);
}

And here's the longer and probably worse way:

public static String formatFloatToString(final float f)
{
  final String s = Float.toString(f);
  int dotPos = -1;
  for(int i=0; i<s.length(); ++i)
    if(s.charAt(i) == '.')
    {
      dotPos = i;
      break;
    }

  if(dotPos == -1)
    return s;

  int end = dotPos;
  for(int i = dotPos + 1; i<s.length(); ++i)
  {
    final char c = s.charAt(i);
    if(c != '0')
      end = i + 1;
  }
  final String result = s.substring(0, end);
  return result;
}
Trinomial answered 18/5, 2013 at 23:3 Comment(11)
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)Valedictorian
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...Trinomial
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.Valedictorian
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?Trinomial
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.Valedictorian
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.Trinomial
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.Overliberal
@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.Trinomial
I don't understand. If you said the formatting didn't matter, why did you spend the time to change it back?Supertax
@Supertax If it's not wrong in any way, it can stay with what I wrote.Trinomial
I agree. But because you changed it back, it sounds like it matters. You actively put the effort in. That's all.Supertax
T
0
public static String fmt(double d) {
    String val = Double.toString(d);
    String[] valArray = val.split("\\.");
    long valLong = 0;
    if(valArray.length == 2) {
        valLong = Long.parseLong(valArray[1]);
    }
     if (valLong == 0)
        return String.format("%d", (long) d);
    else
        return String.format("%s", d);
}

I had to use this because d == (long)d was giving me violation in a SonarQube report.

Tantamount answered 25/7, 2017 at 11:45 Comment(0)
C
0

I am using this for formatting numbers without trailing zeroes in our JSF application. The original built-in formatters required you to specify max numbers of fractional digits which could be useful here also in case you have too many fractional digits.

/**
 * Formats the given Number as with as many fractional digits as precision
 * available.<br>
 * This is a convenient method in case all fractional digits shall be
 * rendered and no custom format / pattern needs to be provided.<br>
 * <br>
 * This serves as a workaround for {@link NumberFormat#getNumberInstance()}
 * which by default only renders up to three fractional digits.
 *
 * @param number
 * @param locale
 * @param groupingUsed <code>true</code> if grouping shall be used
 *
 * @return
 */
public static String formatNumberFraction(final Number number, final Locale locale, final boolean groupingUsed)
{
    if (number == null)
        return null;

    final BigDecimal bDNumber = MathUtils.getBigDecimal(number);

    final NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
    numberFormat.setMaximumFractionDigits(Math.max(0, bDNumber.scale()));
    numberFormat.setGroupingUsed(groupingUsed);

    // Convert back for locale percent formatter
    return numberFormat.format(bDNumber);
}

/**
 * Formats the given Number as percent with as many fractional digits as
 * precision available.<br>
 * This is a convenient method in case all fractional digits shall be
 * rendered and no custom format / pattern needs to be provided.<br>
 * <br>
 * This serves as a workaround for {@link NumberFormat#getPercentInstance()}
 * which does not renders fractional digits.
 *
 * @param number Number in range of [0-1]
 * @param locale
 *
 * @return
 */
public static String formatPercentFraction(final Number number, final Locale locale)
{
    if (number == null)
        return null;

    final BigDecimal bDNumber = MathUtils.getBigDecimal(number).multiply(new BigDecimal(100));

    final NumberFormat percentScaleFormat = NumberFormat.getPercentInstance(locale);
    percentScaleFormat.setMaximumFractionDigits(Math.max(0, bDNumber.scale() - 2));

    final BigDecimal bDNumberPercent = bDNumber.multiply(new BigDecimal(0.01));

    // Convert back for locale percent formatter
    final String strPercent = percentScaleFormat.format(bDNumberPercent);

    return strPercent;
}
Ceil answered 26/10, 2020 at 16:35 Comment(0)
K
0

work with given decimal length...

public static String getLocaleFloatValueDecimalWithLength(Locale loc, float value, int length) {
        //make string from float value
        return String.format(loc, (value % 1 == 0 ? "%.0f" : "%."+length+"f"), value);
    }
Knighterrantry answered 15/2, 2022 at 21:57 Comment(0)
M
0

0.0 -> 0%
1.0 -> 100%
0.1 -> 10%
0.11 -> 11%
0.01 -> 1%
0.111 -> 11.1%
0.001 -> 0.1%
0.1111 -> 11.11%
0.0001 -> 0.01%

".replace()" is added because I was always getting wrong separator

import java.text.NumberFormat

fun Double.formating(): String {
    val defaultFormat: NumberFormat = NumberFormat.getPercentInstance()
    defaultFormat.minimumFractionDigits = 0
    defaultFormat.maximumFractionDigits = 2

    return defaultFormat.format(this).replace(",", ".")
}
Moving answered 30/1, 2023 at 12:50 Comment(0)
S
-1

Here is an answer that actually works (combination of different answers here)

public static String removeTrailingZeros(double f)
{
    if(f == (int)f) {
        return String.format("%d", (int)f);
    }
    return String.format("%f", f).replaceAll("0*$", "");
}
Schizogenesis answered 23/5, 2013 at 18:41 Comment(3)
you did not replace the POINT, for example, "100.0" will be convert to "100."Titanic
if(f == (int)f) takes care of that.Schizogenesis
Fails on f = 9999999999.00Striking
N
-4

The best way to do this is as below:

public class Test {

    public static void main(String args[]){
        System.out.println(String.format("%s something", new Double(3.456)));
        System.out.println(String.format("%s something", new Double(3.456234523452)));
        System.out.println(String.format("%s something", new Double(3.45)));
        System.out.println(String.format("%s something", new Double(3)));
    }
}

Output:

3.456 something
3.456234523452 something
3.45 something
3.0 something

The only issue is the last one where .0 doesn't get removed. But if you are able to live with that then this works best. %.2f will round it to the last two decimal digits. So will DecimalFormat. If you need all the decimal places, but not the trailing zeros then this works best.

Narah answered 9/5, 2012 at 9:4 Comment(3)
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 1Pepillo
thats my point. What if you want the 0.0005 displayed if there is any. You will be rounding it 2 decimal digits.Narah
The OP is asking how to print integer values stored in doubles :)Pepillo
M
-12
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

This will make the string to drop the tailing 0-s.

Mastership answered 26/10, 2011 at 12:39 Comment(2)
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."Stork
Be careful, your solution will convert 1000 into 1, which is wrong.Pepillo

© 2022 - 2024 — McMap. All rights reserved.