I need to convert double to string with given precision. String.format("%.3f", value)
(or DecimalFormat
) does the job, but benchmarks show that it is slow. Even Double.toString
conversion takes about 1-3 seconds to convert 1 million numbers on my machine.
Are there any better way to do it?
UPDATE: Benchmarking results
Random numbers from 0 to 1000000, results are in operations per millisecond (Java 1.7.0_45), higher is better:
Benchmark Mean Mean error Units
String_format 747.394 13.197 ops/ms
BigDecimal_toPlainString 1349.552 31.144 ops/ms
DecimalFormat_format 1890.917 28.886 ops/ms
Double_toString 3341.941 85.453 ops/ms
DoubleFormatUtil_formatDouble 7760.968 87.630 ops/ms
SO_User_format 14269.388 168.206 ops/ms
UPDATE:
Java 10, +ryu, higher is better:
Mode Cnt Score Error Units
String_format thrpt 20 998.741 ± 52.704 ops/ms
BigDecimal_toPlainString thrpt 20 2079.965 ± 101.398 ops/ms
DecimalFormat_format thrpt 20 2040.792 ± 48.378 ops/ms
Double_toString thrpt 20 3575.301 ± 112.548 ops/ms
DoubleFormatUtil_formatDouble thrpt 20 7206.281 ± 307.348 ops/ms
ruy_doubleToString thrpt 20 9626.312 ± 285.778 ops/ms
SO_User_format thrpt 20 17143.901 ± 1307.685 ops/ms
Disclaimer: I only recommend that you use this if speed is an absolute requirement.
On my machine, the following can do 1 million conversions in about 130ms:
private static final int POW10[] = {1, 10, 100, 1000, 10000, 100000, 1000000};
public static String format(double val, int precision) {
StringBuilder sb = new StringBuilder();
if (val < 0) {
sb.append('-');
val = -val;
}
int exp = POW10[precision];
long lval = (long)(val * exp + 0.5);
sb.append(lval / exp).append('.');
long fval = lval % exp;
for (int p = precision - 1; p > 0 && fval < POW10[p]; p--) {
sb.append('0');
}
sb.append(fval);
return sb.toString();
}
The code as presented has several shortcomings: it can only handle a limited range of doubles
, and it doesn't handle NaNs. The former can be addressed (but only partially) by extending the POW10
array. The latter can be explicitly handled in the code.
If you don't need thread-safe code, you can re-use the buffer for a little more speed (to avoid recreating a new object each time), such as:
private static final int[] POW10 = {1, 10, 100, 1000, 10000, 100000, 1000000};
private static final StringBuilder BUFFER = new StringBuilder();
public String format( double value, final int precision ) {
final var sb = BUFFER;
sb.setLength( 0 );
if( value < 0 ) {
sb.append( '-' );
value = -value;
}
final int exp = POW10[ precision ];
final long lval = (long) (value * exp + 0.5);
sb.append( lval / exp ).append( '.' );
final long fval = lval % exp;
for( int p = precision - 1; p > 0 && fval < POW10[ p ]; p-- ) {
sb.append( '0' );
}
sb.append( fval );
return sb.toString();
}