msinko
November 14, 2018, 1:12am
1
At some point in the last few months http.jsonencode started outputting some strange numbers
local temp = {“value”: sysdataarray[index],“key”: datakeyarray[index]};
local dataString = http.jsonencode(temp);
server.log(temp[“value”])
server.log(dataString)
log output:
|[Agent] |39.7|
|[Agent] |{ “value”: 39.700001, “key”: “Trunk Line Temperature” }|
In this case the values are temperature data with a single floating point precision e.g. 39.7
The http.jsonencode output is 39.700001
huh?
some more examples
38.6 = 38.599999
38.4 = 38.400002
38.7 = 38.700001
39.1 = 39.099998
it’s not even consistent…Any ideas?
hugo
November 14, 2018, 5:50am
2
That appears to just be 32 bit single precision float encoding.
The nearest you can encode 39.7 into binary is actually 39.700000762939453125, which is being rounded to 39.700001 for the json encode.
If you encode a number where the mantissa can be exactly represented in binary (eg 39.125) you will see an exact value.
See https://www.h-schmidt.net/FloatConverter/IEEE754.html for the gory details… as to behavior changing, that’s possible as previously we were missing a digit at the end. @rogerlipscombe ?
The extra precision was added to the agent on Sep 19; it was deployed to the developer server on Oct 19 (and one of the production servers on Oct 8).
As Hugo points out, Squirrel uses IEEE-754 single precision binary floating point numbers. They can’t exactly represent most decimal floating point numbers.
server.log()
appears to be truncating to one decimal place; http.jsonencode()
previously rounded to 6 digits of precision. It now rounds to 8 (which is the maximum that IEEE-754 single precision floats can represent).
hugo
November 14, 2018, 6:22pm
4
If you wanted your JSON to show less precision, eg 1 decimal place, you could use the squirrel JSON encoder class here https://developer.electricimp.com/libraries/utilities/jsonencoder but alter it, eg changing this bit:
case "integer":
case "float":
case "bool":
r += val;
break;
to…
case "float":
r += format("%.1f", val);
break;
case "integer":
case "bool":
r += val;
break;
msinko
November 14, 2018, 9:00pm
5
Thank you Hugo and Roger! Much appreciated!