Hello,

I mentioned a while ago the idea of moving a utility that I find quite useful 
from commons-geometry to commons-text, which would be a more appropriate home 
for it. There was not any interest at the time but I've made a few improvements 
to the class and I'd like to try again. The utility in question is the 
DoubleFormats [1] class. This class contains factory methods for producing 
lightweight, thread-safe DoubleFunction<String> instances for converting 
doubles to decimal strings in different formats. The class is specifically 
designed for data output; no localization is performed. It is used in 
commons-geometry to provide a way to control the precision and formatting of 
double values in text-based geometric data formats such as OBJ. I've found that 
although the JDK provides a number of different ways to format doubles (eg, 
String.format, DecimalFormat, BigDecimal, etc), none of them have fit the 
requirements for performant, thread-safe data output. Hence, the reason for 
this class.

Below are examples of each of the types of formats available and some outputs. 
The arguments passed to each method are the precision (maximum number of 
non-zero decimal digits) and min exponent (base 10 exponent for the smallest 
non-zero number that should be represented).

// plain decimal representation; no scientific format
DoubleFunction<String> plain = DoubleFormats.createPlain(5, -3);
plain.apply(1);         // 1.0
plain.apply(1e10);      // 10000000000.0
plain.apply(1234.567);  // 1234.6
plain.apply(0.00356);   // 0.004

// scientific format
DoubleFunction<String> sci = DoubleFormats.createScientific(5, -3);
sci.apply(1);           // 1.0
sci.apply(1e10);        // 1.0E10
sci.apply(1234.567);    // 1.2346E3
sci.apply(0.00356);     // 4.0E-3

// engineering format
DoubleFunction<String> eng = DoubleFormats.createEngineering(5, -3);
eng.apply(1);           // 1.0
eng.apply(1e10);        // 10.0E9
eng.apply(1234.567);    // 1.2346E3
eng.apply(0.00356);     // 4.0E-3

// default format; uses the Double.toString() convention of representing
// numbers less that 10^-3 or greater than 10^7 using scientific format and
// other numbers using plain decimal format
DoubleFunction<String> def = DoubleFormats.createDefault(5, -3);
def.apply(1);           // 1.0
def.apply(1e10);        // 1.0E10
def.apply(1234.567);    // 1234.6
def.apply(0.00356);     // 0.004


The performance of all of these methods is comparable to DecimalFormat or 
BigDecimal. The benchmark output below shows the results of formatting 10000 
double values using standard Double.toString(), a simple BigDecimal conversion, 
DecimalFormat (single instance), and a function returned from 
DoubleFormats.createDefault(). Double.toString() is the clear winner but the 
rest are all quite close.

Benchmark                                          (size)  Mode  Cnt        
Score        Error  Units
DoubleFormatsPerformance.doubleToString             10000  avgt    5  
3837610.399 ±  62668.705  ns/op
DoubleFormatsPerformance.bigDecimal                 10000  avgt    5  
6279807.365 ±  93566.619  ns/op
DoubleFormatsPerformance.decimalFormat              10000  avgt    5  
5787717.633 ± 168626.950  ns/op
DoubleFormatsPerformance.doubleFormatsDefault       10000  avgt    5  
5779534.166 ±  69496.434  ns/op

Please let me know if there is any interest in moving this class to 
commons-text. It's primary advantages are that it is
-thread-safe (unlike DecimalFormat),
-performant (unlike String.format()), and
-allows a variety of output formats (unlike BigDecimal).

I would also be open to discussion and improvements on the 
design/implementation.

Regards,
Matt J

[1] 
https://github.com/apache/commons-geometry/blob/master/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/DoubleFormats.java

Reply via email to