# Floating point math limitations

I think I may be running into fp limitations:
`
function timeTest(){
local t;

t = 10;
t = t + 0.123;
server.log(format("%.3f", t));

t = time().tofloat();
t = t + 0.123;
server.log(format("%.3f", t));
}
`
produces:

2015-12-28 15:06:06 UTC+0 [Agent] 10.123 2015-12-28 15:06:06 UTC+0 [Agent] 1451315200.000
I 'm trying to form an "epoch" value as per the Initial State API spec, which calls for a number with fractional seconds after the decimal point. I'm guessing the 0.123 I'm trying to add in the above example is beyond the "dynamic range" of the squirrel fp? If so how might I get the value I need?

floats are only 32 bits, so something has to give.

Since you are only passing it to Initial State, and not performing any operations on it, you could concatenate the float value and fractional value together as strings.
eg
`"{\"key\": \"temp\", \"value\": 32, \"epoch\":"+time().tofloat()+"."+millis+" }"`

You can add a fractional part to ISO8601. I do this all the time. It’s then much easier to deal with, because it’s passed as a string, not a number. That way, you don’t have to bypass your json encoder.
eg
`// here is my timestamp routine // given utc integer, returns a valid ISO8601 string (with no time zone) function ts(t=null){ local d = (t==null)?date():date(t); return format("%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", d.year, d.month+1, d.day, d.hour, d.min, d.sec,d.usec/1000); }`

Thanks! My mindset is still firmly typed so I was thinking it required an actual float. :">

I now wish I’d titled this topic “making a timestamp on the device” as it’s proving to be non-trivial. @coverdriven, it seems (at least for imp001) that the undocumented d.usec always returns zero.

As I need to timestamp the data in the device (where it’s sampled) to a resolution of 1msec or less, with the correct epoch before the decimal point, there doesn’t seem to be any synchronous timing information that can be “stitched” on after the decimal point. Tests I did showed that hardware.millis() hardware.micros() are asynchronous.

To get things going for the time being I constantly check for a change in seconds then grab .millis() as reference and use it to normalise subsequent millisecond readings from zero. It’s not very pretty though.

It depends a bit what properties you want from your timestamp. There are currently three timebases on the imp: the centisecond timer, the microsecond timer, and the RTC. The centisecond timer is used for imp.wakeup() and imp.sleep(); the microsecond timer for hardware.micros() and hardware.millis(); and the RTC for time(), server.sleepfor(), and the other “deep sleep” calls.

The three timebases are asynchronous - the centisecond timer and microsecond timer are in theory both derived from the same crystal, but they’re counted in different ways, so they don’t tick in complete lockstep.

The centisecond and microsecond timers are monotonic per boot – they are zeroed by restarts and deep sleeps, so are not monotonic over all time. The RTC timer isn’t monotonic either, because the imp-server (which has a better timebase than the imp) can reset its idea of time, by lurching it in either direction.

In the future (release-33 and later), there will be just two timebases: the centisecond timer will be run from the microsecond timer.

In general the unaided imp hardware simply can’t determine wall time to an accuracy of 1ms. The crystal timebase is only guaranteed to 30ppm, which is plus-or-minus 2-3 seconds per day.

For your purposes options might include (1) determine the phase of the mícrosecond timer wrt the RTC timer, use that to add figures past the decimal point, result isn’t monotonic (sounds like this is what you’re currently doing); or (2) read the RTC timer once, then use just the microsecond timer to determine how many seconds have elapsed since then, result is monotonic and even but diverges increasingly from wall time.

Peter

@iceled, I use date() and date.usec on the agent. It works for both imp001 and imp002. I don’t know about the device though. I use millis() and micros() there and send time deltas back to the agent.