Rounding Bug in pace calculation

Hi, I probably have a rounding bug in my pace calculation. If I double check the calculated pace vs Garmin's pace it is most of the time 1 second behind. Here is my code:

function fmtSecs(secs, isPace) {

if (secs == null) {
return "---";
}

var s = secs.toLong();
var hours = 0;
if (!isPace) { // Performance boost: assumption: a pace value does never has hours!!
hours = s / 3600;
s -= hours * 3600;
}

var minutes = s / 60;
s -= minutes * 60;

var fmt;
if (hours != 0) {
fmt = "" + hours + ":" + minutes.format("%02d") + ":" + s.format("%02d");
} else {
fmt = "" + minutes + ":" + s.format("%02d");
}

return fmt;
}


The rounding bug is probably in these lines:
var minutes = s / 60;
s -= minutes * 60;


Is there a better implementation? What do you think?
  • If secs is a fixed point (not floating point), you should not have any rounding error.

    If secs is floating point, the secs.toLong() call is going to truncate your result. If you want to round up for values over the half second, you'd need to use Math.round(secs).toLong(). If you are trying to support devices with ConnectIQ 1.2 and earlier firmware, you can get the same result by writing (secs + 0.5).toLong().

    Travis
  • Hi Travis,

    thanks for pointing me in the right direction. How can I determine if it's a fixed point or a floating point?

    I wanted to println this value, but I currently have huge problems to get the simulator running: I playback a .fit file but e. g. no average speed is given (it's null). Is this a known problem? I have got it for a while now and it makes development almost impossible.
  • How can I determine if it's a fixed point or a floating point?

    If you want, you could just always apply the rounding and it would work just fine either way. That would be the robust solution.

    If you really only want to do the rounding if it is necessary, there are a few ways to figure out the type. The first way would be to look at your code and trace the origin of the secs parameter. When you find the origin, you need to look into the documentation to see the type. The second way would be to add some debug code that checks if (secs instanceof Lang.Float || secs instanceof Lang.Double). The third way to do it would be to write code that assumes it is not floating point, and see if it crashes...

    function fmtSecs(s) {

    if (s == null) {
    return "---";
    }

    // s = (s + 0.5).toNumber(); // put this back if `s' might be a floating-point

    var hours = s / 3600;

    // the modulus operator isn't supported for floating-point types, so this would throw
    // an exception if `s` is not one of the integral types.
    var mins = (s % 3600) / 60;
    var secs = s % 60;

    if (hours != 0) {
    return Lang.format("$1$:$2$:$3$", [
    hours,
    mins.format("%02d"),
    secs.format("%02d")
    ]);
    } else {
    return Lang.format("$1$:$2$", [
    mins,
    secs.format("%02d")
    ]);
    }
    }
  • I playback a .fit file but e. g. no average speed is given (it's null).

    After you start simulation via the menu, are you starting the timer via the menu or by clicking on the simulated device Start/Stop button?

    Is this a known problem?

    I don't know- I don't have access to the Garmin bug tracker.

    If it is a problem for you, write up a procedure to demonstrate the problem, and post about it in the Bug Reports forum. My guess is that you're not telling the device to start/stop, and it doesn't do any averages until the activity has started (just like a real device).

    Travis
  • Hi Travis,

    thanks to your coding snippet and hints I found out that the secs is a float. I have done following modifications to my code now:
    I inserted the +0.5 to correct the rounding bug. Thanks for that. But I also changed the toLong() to toNumber() which is a little more memory friendly.

    Thanks for your help.