6

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?

ravemir
  • 905
  • 2
  • 11
  • 27
  • 1
    Your problem is that the number doesn't fit in the format. Try "##############.####". – Hot Licks Dec 12 '12 at 16:24
  • I tried that too then, but checked anyway in case I had forgotten. A format "####################.####" (with 7 extra digits) nets the same result. – ravemir Dec 12 '12 at 18:19
  • Are you using the java.util version of DecimalFormat or an Android one? – Hot Licks Dec 12 '12 at 18:42
  • I don't think there is a DecimalFormat class for Android, as the only one on the [documentation](http://developer.android.com/reference/java/text/DecimalFormat.html) is from java. – ravemir Dec 12 '12 at 18:53
  • Just thought I'd ask -- Android has their own versions of several classes, and it can be a source of confusion. – Hot Licks Dec 12 '12 at 19:24
  • Question: Are you *sure* that the above "line" variable is what is getting printed? – Hot Licks Dec 12 '12 at 19:25
  • 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. – ravemir Dec 12 '12 at 19:29
  • Possible duplicate of http://stackoverflow.com/questions/2546147/java-convert-scientific-notation-to-regular-int ? – Walialu Dec 21 '12 at 12:17
  • 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. – ravemir Dec 21 '12 at 12:23
  • 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. – ravemir Dec 21 '12 at 16:15

3 Answers3

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

Henry
  • 40,427
  • 6
  • 56
  • 72
  • 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. – ravemir Dec 21 '12 at 15:57
  • 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. – ravemir Dec 21 '12 at 16:47
  • 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. – ravemir Dec 21 '12 at 16:48
  • I am not able to award the bounty right now, but feel free to remind me in case I forget. ;) – ravemir Dec 21 '12 at 18:03
  • Almost forgot to award it to you. Enjoy your rep! – ravemir Dec 27 '12 at 15:30
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.
...
ravemir
  • 905
  • 2
  • 11
  • 27
0

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

Adrián Rodríguez
  • 1,853
  • 13
  • 16