Sitelet https://howtodoinjava.com/java/string/float-to-string-format-decimal-points/

Convert Float to String in Java with N Decimal Places

Convert a float to a String in Java with Float.toString() or String.valueOf(), and format it to 2 or N decimal places with String.format() or DecimalFormat. Learn the rounding rules, the locale trap and why 2.675f becomes 2.67.

Java String

To convert a float to a String in Java, we call Float.toString(value) or String.valueOf(value), and to get a fixed number of decimal places, such as 2, we call String.format(“%.2f”, value) or use a DecimalFormat with the pattern “0.00”. The first two methods return the shortest text that identifies the float, whereas the formatting methods round the value to the digits we ask for.

We convert a float to text whenever a number leaves our code, for example when a fitness app shows a running distance of 19.5 km or writes a sensor value to a CSV file.

The following example converts one float value with each method, with the result of each line as a comment.

float distance = 19.5f;

String text = Float.toString(distance);                               // "19.5"
String same = String.valueOf(distance);                               // "19.5"
String twoDecimals = String.format("%.2f", distance);                 // "19.50"
String formatted = "%.2f".formatted(distance);                        // "19.50" (Java 15+)
String pattern = new DecimalFormat("0.00").format(distance);          // "19.50"
String noTrailingZeros = new DecimalFormat("0.##").format(distance);  // "19.5"
String fixedLocale = String.format(Locale.ROOT, "%.2f", distance);    // "19.50" on every machine
String german = String.format(Locale.GERMANY, "%.2f", distance);      // "19,50"

Notice the last two lines. String.format() and DecimalFormat use the default locale of the JVM, so the same code prints 19,50 on a server with a German locale.

Next, we look at the exact text that Float.toString() returns and at null values. After that, we format to N decimal places with String.format() and DecimalFormat, and see why 2.675f rounds to 2.67.

1. Float.toString() and String.valueOf()

The method Float.toString(float) returns the shortest decimal text that converts back to the same float value. The method String.valueOf(float) calls Float.toString(), so both give the same result, and so does string concatenation, e.g. “Distance: ” + distance + ” km” gives “Distance: 19.5 km”.

1.1. What the Text Looks Like

The text follows fixed rules that depend on the size of the value. A value from 0.001 up to, but not including, 10,000,000 is printed in plain notation and always has at least one digit after the dot, so 100f becomes “100.0”. Smaller and larger values use scientific notation, where E means “times 10 to the power of”.

float valueFloat.toString(value)
3.1415927f“3.1415927”
-3.1415927f“-3.1415927”
100f“100.0”
0.001f“0.001”
0.0001f“1.0E-4”
9999999f“9999999.0”
1.0E7f“1.0E7”
-0.0f“-0.0”
Float.NaN or 0.0f / 0.0f“NaN”
Float.POSITIVE_INFINITY“Infinity”
0.1f + 0.2f“0.3”
1.1f 3*“3.3000002”

The last two rows show the limits of a float. The sum 0.1f + 0.2f prints as 0.3, because the nearest float to the true sum is also the nearest float to 0.3. The product 1.1f 3* is not, so it prints 3.3000002. Neither method throws an exception for NaN or infinity.

We use Float.toString() for text that a program reads back, such as a JSON value or a log line, because Float.parseFloat() returns the same float from it. For text that a person reads, we format the value as in section 2.

1.2. Converting a Float That Can Be null

A Float object can be null, for example an optional discount field in an order form. The methods handle null in different ways, and only one of them throws an exception.

Float discount = null;

String viaValueOf = String.valueOf(discount);              // "null"
String viaConcat = "Discount: " + discount;                // "Discount: null"
String viaObjects = Objects.toString(discount, "none");    // "none"
String crash = Float.toString(discount);                   // NullPointerException (unboxing)
String cut = String.format("%.2f", discount);              // "nu"

The method Float.toString(float) accepts a primitive, so Java unboxes the Float first, and unboxing null throws a NullPointerException. The String.valueOf(Object) overload returns the text “null”, which is rarely what a user should see.

The last line is a trap. For a null argument, %.2f prints the text “null”, and the precision .2 cuts it to 2 characters, so we get “nu” without any error. The safe version checks for null before formatting.

String safe = discount == null ? "none" : String.format("%.2f", discount);   // "none"

2. Formatting a float to N Decimal Places

The methods in section 1 print as many digits as the value needs, which is not what a report or a screen wants. To show 2 decimals every time, we choose between String.format(), which uses printf-style format specifiers, and DecimalFormat, which uses a pattern. Both round the value.

2.1. Using String.format() or formatted()

The format specifier %.nf prints a float with n digits after the decimal point. Since Java 15, we can also call formatted() on the format string, e.g. “%.2f”.formatted(value), which gives the same result. The flags before the precision add a minimum width, zero padding, a sign or grouping separators, as shown in printf-style formatting.

float pi = 3.1415927f;

String two = String.format("%.2f", pi);                     // "3.14"
String none = String.format("%.0f", pi);                    // "3"
String four = String.format("%.4f", pi);                    // "3.1416"
String six = String.format("%f", pi);                       // "3.141593" (default precision is 6)
String width = String.format("%8.2f", pi);                  // "    3.14" (8 characters, right-aligned)
String left = String.format("%-8.2f", pi);                  // "3.14    "
String zeros = String.format("%08.2f", pi);                 // "00003.14"
String sign = String.format("%+.2f", pi);                   // "+3.14"
String grouped = String.format(Locale.US, "%,.2f", 1234567.5f);   // "1,234,567.50"
String scientific = String.format(Locale.US, "%.2e", 12345.678f); // "1.23e+04"

When the value has more digits than the precision, String.format() rounds half up, so 0.125f becomes “0.13”. A negative value that rounds to zero keeps its sign, so -0.001f becomes “-0.00”, and NaN stays “NaN”.

2.2. Choosing the Locale

Without a locale argument, String.format() uses the default locale of the JVM, which comes from the operating system. The locale decides the decimal separator and the grouping separator.

CodeResult
String.format(Locale.US, “%.1f”, 21.75f)“21.8”
String.format(Locale.FRANCE, “%.1f”, 21.75f)“21,8”
String.format(Locale.GERMANY, “%,.2f”, 1234567.5f)“1.234.567,50”
String.format(Locale.ROOT, “%.2f”, 21.75f)“21.75”

For example, a nightly job writes sensor values to a CSV file and works on every developer laptop. On a production server with a German locale, the job writes 21,75, and the comma breaks the CSV columns. So for text that a program reads, we pass Locale.ROOT or Locale.US, and for text that a user reads, we pass the user’s locale. The Locale API examples show how to build a Locale for a user.

2.3. Using DecimalFormat

A DecimalFormat formats numbers with a pattern. In the pattern, 0 always prints a digit, even a zero, and # prints a digit only when it is not a trailing zero. A comma marks where the grouping separator goes.

DecimalFormat twoDigits = new DecimalFormat("0.00");

String pi = twoDigits.format(3.1415927f);                         // "3.14"
String five = twoDigits.format(5f);                               // "5.00"
String trimmed = new DecimalFormat("0.##").format(5f);            // "5"
String half = new DecimalFormat("0.##").format(0.5f);             // "0.5"
String grouped = new DecimalFormat("#,##0.00").format(1234567.5f); // "1,234,567.50"

By default, DecimalFormat rounds with RoundingMode.HALF_EVEN, which rounds a value in the exact middle to the even neighbor. So 0.125f becomes “0.12” with DecimalFormat but “0.13” with String.format(). Half-even rounding is also called banker’s rounding, because it does not push the sum of many rounded values up. We change the mode with setRoundingMode().

DecimalFormat halfUp = new DecimalFormat("0.00");
halfUp.setRoundingMode(RoundingMode.HALF_UP);
String up = halfUp.format(0.125f);                                // "0.13"

DecimalFormat down = new DecimalFormat("0.00");
down.setRoundingMode(RoundingMode.DOWN);
String cut = down.format(3.999f);                                 // "3.99"

DecimalFormat german = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.GERMANY));
String withComma = german.format(3.1415927f);                     // "3,14"

A DecimalFormat object is not thread-safe, so we create one per thread or per call, and never share one in a static field between request threads. Passing a null Float to format() throws an IllegalArgumentException with the message “Cannot format given Object as a Number”. For other rounding methods, such as Math.round(), read rounding to N decimal places.

3. Why 2.675f Rounds to 2.67

A float stores numbers in binary with 24 significant bits, which is about 7 decimal digits. Most decimal fractions, such as 0.1 or 2.675, have no exact binary form, so Java stores the nearest float. Float.toString() hides the difference, because it prints the shortest text that maps back to the stored value.

The constructor new BigDecimal(double), which also accepts a float, shows the exact stored value. For 2.675f, the stored value is slightly smaller than 2.675, so half-up rounding to 2 decimals gives 2.67.

Number line from 2.670 to 2.680 with the midpoint 2.675 marked. The stored float value 2.6749999523... lies a little left of the midpoint, so String.format("%.2f") and DecimalFormat round it down to 2.67. BigDecimal created from the text "2.675" sits on the midpoint and HALF_UP rounds it to 2.68.
The float 2.675f is stored a little below 2.675, so rounding the stored value gives 2.67, whereas rounding the text “2.675” gives 2.68.
float value = 2.675f;

BigDecimal exact = new BigDecimal(value);              // 2.6749999523162841796875
String viaFormat = String.format("%.2f", value);       // "2.67"
String viaDecimalFormat = halfUp.format(value);        // "2.67" (RoundingMode.HALF_UP)
String viaBigDecimal = new BigDecimal(Float.toString(value))
    .setScale(2, RoundingMode.HALF_UP)
    .toPlainString();                                  // "2.68"

The BigDecimal line works on the decimal text “2.675”, which is what the user typed, so it rounds the way people expect. The same thing happens with 0.35f, where String.format(“%.1f”, 0.35f) returns “0.3” and the BigDecimal version returns “0.4”.

The 7-digit limit also shows up in large numbers. For example, 123456789f is stored as 123456792, so Float.toString() returns “1.2345679E8” and String.format(“%.0f”, 123456789f) returns “123456792”. Widening a float to double does not bring the lost digits back, e.g. (double) 0.1f is 0.10000000149011612.

For money and other values that must be exact in decimal, we use BigDecimal from the start, not float. For measurements such as distances and temperatures, a float or a double is fine, and we round only when we format. The comparing float and double values article explains why we also never compare such values with ==.

4. Converting Without Scientific Notation

Float.toString() switches to scientific notation below 0.001 and from 10,000,000 up, which looks odd in a report. String.format(“%.2f”, value) never uses scientific notation, but it forces a fixed number of decimals. When we want the plain digits with no trailing zeros, we go through BigDecimal.

static String toPlainString(float value) {
  if (Float.isNaN(value) || Float.isInfinite(value)) {
    return Float.toString(value);
  }
  return new BigDecimal(Float.toString(value)).stripTrailingZeros().toPlainString();
}

The helper returns NaN and infinity as they are, because new BigDecimal() throws a NumberFormatException for them.

String big = toPlainString(1.0E10f);       // "10000000000"
String small = toPlainString(1.0E-4f);     // "0.0001"
String trimmed = toPlainString(19.50f);    // "19.5"
String whole = toPlainString(100f);        // "100"
String notANumber = toPlainString(Float.NaN);   // "NaN"

5. Which Method to Use to Convert Float to String

The right method depends on who reads the text. A program needs text it can parse back without loss, whereas a person needs a fixed number of decimals in their own locale.

GoalMethodExample result for 19.5f
Text for a program, log or JSONFloat.toString(value) or String.valueOf(value)“19.5”
Fixed decimals for a personString.format(locale, “%.2f”, value)“19.50”
Fixed decimals, reused often or with groupingnew DecimalFormat(“#,##0.00”)“19.50”
No trailing zerosnew DecimalFormat(“0.##”)“19.5”
Rounding the way people expect from the decimal textnew BigDecimal(Float.toString(value)).setScale(2, RoundingMode.HALF_UP)“19.50”
Large or small values without EtoPlainString() helper from section 4“19.5”

For the opposite direction, from text to a number, the methods Float.parseFloat() and Float.valueOf() follow the same rules as converting a String to an int, and both throw a NumberFormatException for invalid text.

6. Conclusion

Float.toString() and String.valueOf() convert a float to the shortest text that maps back to the same value, and they switch to scientific notation below 0.001 and from 10,000,000 up. For a Float that can be null, we check for null first, since Float.toString() throws a NullPointerException and %.2f prints “nu”.

To show a fixed number of decimal places, we use String.format(“%.2f”, value) or a DecimalFormat such as “0.00”, and we pass a locale so that the decimal separator does not depend on the server. The two methods round differently, half up and half even.

Both methods round the stored binary value, so 2.675f becomes 2.67. When the rounding must follow the decimal text, we create a BigDecimal from Float.toString(value) and round that.

7. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.