Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Formatting Double to print without scientific notation using DecimalFormat

I have been reading timestamp values from sensor readings, but since they are provided in nanoseconds, I thought I would cast them to double and make the conversion. The resulting number is a 17 digit value, plus the separator.

Trying to print it directly results in scientific notation, which I don't want, so I use a DecimalFormat class to output it to an expected value of 4 decimal places. The problem is, even though the debugger shows a number of 17 decimal digits, even after the 'doubleValue()' call, the output string shows me a number of 15 digits.

Code:

...
Double timestamp = (new Date().getTime()) +       // Example: 1.3552299670232847E12
            ((event.timestamp - System.nanoTime()) / 1000000D);
DecimalFormat dfmt = new DecimalFormat("#.####");

switch(event.sensor.getType()){
    case Sensor.TYPE_LINEAR_ACCELERATION:
    case Sensor.TYPE_ACCELEROMETER:
        accel = event.values.clone();
        String line = "A" + LOGSEPARATOR +              
            dfmt.format(timestamp.doubleValue()) + // Prints: 1355229967023.28
...

I thought this might be an android precision problem, but the debugger has the formatter showing the wrong precision as well. I have tested this in a local java program and both calls have the same amount of digits.

Is this a DecimalFormat bug/limitation? Or am I doing something wrong?

like image 349
ravemir Avatar asked Dec 12 '12 15:12

ravemir


People also ask

How do I print a double value without scientific notation using Java?

double dexp = 12345678; System. out. println("dexp: "+dexp);

How do you turn off scientific notation in Java?

In the Format Cells window, (1) select the Number category, (2) set the number of decimal places to 0, and (3) click OK. Now the scientific notation is removed.

Can you format a double in Java?

In Java, we can use String. format or DecimalFormat to format a double , both support Locale based formatting.


2 Answers

A double in Java has a mantissa of only 52 bit (counting the hidden 1 its 53 bit). This is equivalent to 15-16 decimal places (53*log10(2)). Every digit after this is kind of random and therefore it makes sense for the conversion function to cut the output after 15 decimal places.

Since you do not need the large number range that double provides, why not keep the value as long? This would give you 63 significant bits (64 -1 for the sign).

like image 63
Henry Avatar answered Sep 25 '22 01:09

Henry


There is indeed a difference between Java's and Android's DecimalFormat class, and they output different results, despite taking the exact same arguments.

This was enough for me to try Henry's approach, and now that I have I see that I have gained an extra 2 places of precision. I am also confident that the values are calculated accurately, as only sums and multiplications are involved.

This is the modified code I ended up using:

...
long javaTime = new Date().getTime();
long nanoTime = System.nanoTime();
long newtimestamp = javaTime * 1000000 +            // Compute the timestamp
            (event.timestamp - nanoTime);           // in nanos first
String longStr = Long.valueOf(newtimestamp).toString();
String tsString = longStr.substring(0, longStr.length()-6) +// Format the output string
            "." + longStr.substring(longStr.length()-6);    // to have the comma in the
                                                            // correct space.
...
like image 30
ravemir Avatar answered Sep 26 '22 01:09

ravemir