Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast double to string conversion with given precision

I need to convert double to string with given precision. String.format("%.3f", value) (or DecimalFormat) does the job but benchmarks show that it slow even in comparison to not very fast Double.toString conversion (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)

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

                                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
like image 370
valodzka Avatar asked May 11 '12 14:05

valodzka


2 Answers

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.

like image 132
NPE Avatar answered Oct 11 '22 13:10

NPE


If you need both speed and precision, I've developed a fast DoubleFormatUtil class at xmlgraphics-commons: http://xmlgraphics.apache.org/commons/changes.html#version_1.5rc1

You can see the code there: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/util/DoubleFormatUtil.java?view=markup

It's faster than both DecimalFormat/BigDecimal, as fast as Double.toString, it's precise, it's well tested. It's licensed under Apache License 2.0, so you can use it as you want.

like image 29
Julien Aymé Avatar answered Oct 11 '22 13:10

Julien Aymé