Formatting Double to print without scientific notation using DecimalFormat
Asked Answered
H

3

7

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?

Halpin answered 12/12, 2012 at 15:11 Comment(10)
Your problem is that the number doesn't fit in the format. Try "##############.####".Absorptance
I tried that too then, but checked anyway in case I had forgotten. A format "####################.####" (with 7 extra digits) nets the same result.Halpin
Are you using the java.util version of DecimalFormat or an Android one?Absorptance
I don't think there is a DecimalFormat class for Android, as the only one on the documentation is from java.Halpin
Just thought I'd ask -- Android has their own versions of several classes, and it can be a source of confusion.Absorptance
Question: Are you sure that the above "line" variable is what is getting printed?Absorptance
Positive: I am even checking the output by Inspecting (Ctrl+I, it evaluates the selected code with the currrent state values) the value of the output of the formatter in a debugging session. That line obviously goes inside a file, but I'd have to be mad to open/close a file everytime I wanted to test. And yes, I coincidentally checked that, since I used the DateFormatter for the same program, and came across the colliding classes.Halpin
Possible duplicate of #2546647 ?Limulus
Nope: I said I can convert it, but lose two decimal places while doing it, which doesn't make sense, since the data is there.Halpin
Not sure what that means. Do you mean setting the tick next to responses to my questions? I'm pretty sure I ticked it when the answer did it for me, but I'll check again. I can only accept answers (or make my own) when I solve questions, though.Halpin
H
1

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.
...
Halpin answered 21/12, 2012 at 18:3 Comment(0)
H
2

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).

Hills answered 21/12, 2012 at 15:21 Comment(5)
This was my reasoning: I need to convert nanoseconds to miliseconds, computed with two longs. But I hadn't thought the other way round: changing miliseconds to nanoseconds, which doesn't require floating point data. This way I can keep the precision from the longs and I just need to move the comma over 6 places to the left when printing it out. Let me test this and I'll get back to you.Halpin
It worked! Not only did I get the other two places, but an extra 2 aswell. The weird thing is, I was expecting that the last two chars computed with the previous method were just random numbers, but they match the ones inside my newly calculated long value.Halpin
I'll grant you the bounty and add my own answer formatted. I feel kind of dumb, though, because I thought of doing something similar to this, but didn't bother/thought it was dodgy.Halpin
I am not able to award the bounty right now, but feel free to remind me in case I forget. ;)Halpin
Almost forgot to award it to you. Enjoy your rep!Halpin
K
1

Was doing some research with String.format aswell with same results.

Double timestamp = 1.3552299670232847E12;
System.out.println("it was " + timestamp);
System.out.println("and now " + String.format("%.4f", timestamp));

And this is the output:

12-12 15:48:58.255: I/System.out(2989): it was 1.3552299670232847E12
12-12 15:48:58.255: I/System.out(2989): and now 1355229967023,2800

Maybe you're right and it's an Android precision problem as if you try it in Java, the output is correct: http://ideone.com/PBOiet

I'll keep googling...

Kassiekassity answered 12/12, 2012 at 15:56 Comment(0)
H
1

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.
...
Halpin answered 21/12, 2012 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.