Site iconJava PDF Blog

Optimising conversion from float to String in Java

Lets say you have a very specific use case – you want to convert a float to a String in Java, and if the float ends in .0, you want to chop it off. I have this use case, (I was investigating reducing output file size on our PDF to HTML5 Converter) and as I had fun playing with this, I decided it would be worth sharing.

I could convert the float to a String, find the index of the decimal if it has one, then if the numbers after it are all 0 then chop them off, but this is messy and not my style. It makes far more sense to work this out numerically.

Here’s my set of floats I will be testing on.

float[] floats = {1.75f, 0.01f, 0.00f, 6.0f, 123.456f, 1234.0f, 000.000f};

The method I am going to use is to take my float value, cast it to an integer to chop off any decimal places, then minus the casted value from the original to leave me with only the decimal places. If the result is 0, then I output the int as a String, otherwise I output the float as a String. Here’s how that may look:

private static String convert(float val) {
    return (val - (int)val == 0) ? "" + (int)val : "" + val;
}

To measure its efficiency, I will calculate the time taken to run the converter over each of the floats in my test set 100,000,000 times.

Just for comparison, here’s how long it takes using the messy String manipulation method I spoke about earlier.

1.75 in 8618 millis.
0.01 in 9596 millis.
0 in 5279 millis.
6 in 7690 millis.
123.456 in 12069 millis.
1234 in 8913 millis.
0 in 5278 millis.

And here’s the results for my method.

return (val - (int)val == 0) ? "" + (int)val : "" + val;
1.75 in 9467 millis.
0.01 in 9995 millis.
0 in 942 millis.
6 in 940 millis.
123.456 in 12958 millis.
1234 in 1940 millis.
0 in 952 millis

For the floats that don’t have their decimal places removed, the time taken is on par with with messy String method.
What’s really interesting is that for the results that use the int, it’s much faster. It shows that the majority of the time taken is from converting a float into a String.

So how about if we try alternate ways of converting from a float to a String?

return (val - (int)val == 0) ? "" + (int)val : String.valueOf(val);
1.75 in 8042 millis.
0.01 in 8788 millis.
0 in 841 millis.
6 in 838 millis.
123.456 in 11316 millis.
1234 in 1550 millis.
0 in 837 millis.

Marginally better – interesting!

return (val - (int)val == 0) ? "" + (int)val : Float.toString(val);
1.75 in 8183 millis.
0.01 in 8514 millis.
0 in 870 millis.
6 in 838 millis.
123.456 in 11373 millis.
1234 in 1541 millis.
0 in 835 millis.

About the same as String.valueOf(val) – perhaps it’s implemented in the same way? After having looked, I can confirm that one does indeed call the other.

What about if we try the same thing on converting an int to a String?

return (val - (int)val == 0) ? Integer.toString((int)val) : Float.toString(val);
1.75 in 8085 millis.
0.01 in 8567 millis.
0 in 1711 millis.
6 in 1707 millis.
123.456 in 11261 millis.
1234 in 2654 millis.
0 in 1909 millis.

Wow, much slower!

return (val - (int)val == 0) ? String.valueOf((int)val) : Float.toString(val);
1.75 in 8079 millis.
0.01 in 8504 millis.
0 in 1917 millis.
6 in 1907 millis.
123.456 in 11435 millis.
1234 in 2651 millis.
0 in 1907 millis.

Again, much slower, “” + int and Float.toString(float) are clearly the way to go!
..Or are they?

Casting a float to an int is very quick. If we only really want 2 decimal places, what if…

result = (val - (int)val == 0) ? "" + (int)val : "" + (int)val + '.' + ((int)(val*100) - (int)val * 100);
1.75 in 1965 millis.
0.1 in 1586 millis.
0 in 1011 millis.
6 in 1010 millis.
123.45 in 2346 millis.
1234 in 1908 millis.
0 in 1006 millis.

Wow, cool!

Here’s the source if you want to have a play yourself:

import java.util.Calendar;

public class TidyTest {
public static void main (String[] args) {

float[] floats = {1.75f, 0.01f, 0.00f, 6.0f, 123.456f, 1234.0f, 000.000f};

for (float f : floats) {
System.out.println(leonTidy(f));
}

}

private static String leonTidy(float val) {

Calendar c = Calendar.getInstance();
long start = c.getTimeInMillis();
String result = "";

for (int i = 0; i < 100000000; i++)
result = (val - (int)val == 0) ? "" + (int)val : "" + val;

c = Calendar.getInstance();
long timeTaken = c.getTimeInMillis() - start;
return result + " in " + timeTaken + " millis.";
}

}