Large numbers for dummies?

I need to process a large number (specifically, an epoch time expressed in milliseconds). It's coming in to my board as JSON. Best I can figure, when I parse it out as a JSON object it's being treated as a double. But because it's so big, I cannot get an accurate value. I need seconds resolution, not milliseconds, so I'm okay with truncating, but I can't even get that to to work. In theory, a double or an unsigned long should be large enough to hold the value in seconds, but nothing is big enough for millis.

The code below is the closest I've gotten, but it is weirdly off.

#include <ArduinoJson.h>
String testData = "{\"node1\": {\"timer\":[{\"fire_time\":1559885523000.0}]}}

const size_t CAPACITY = 2*JSON_ARRAY_SIZE(0) + 2*JSON_ARRAY_SIZE(2) + 3*JSON_OBJECT_SIZE(2) + 4*JSON_OBJECT_SIZE(4) + 200;

void setup() {

  Serial.begin(115200);  
}

void loop() {
  StaticJsonDocument<CAPACITY> doc;
  DeserializationError err = deserializeJson(doc, testData);
  //JsonObject Kitchen_Speaker_timer_0 = doc["Kitchen Speaker"]["timer"][0];
  unsigned long fire_time = doc["node1]["timer][0]["fire_time"]/1000;
  Serial.println(fire_time);
  
}

What I get out of that is: 155988552

which is weird. As I suppose it would be as I'm dealing with out-of-bounds values. But truncating like that is not what I expected at all.

Any ideas how to handle this?

What kind of Arduino are you using?

For most Arduino controllers, a double is a float, which means 4 bytes for storage, or roughly 3.4E+/-38. However, the number of significant digits (e.g., precision) is 6 or 7 digits. So while it could print out a number with 38 digits, only the first 6 or 7 mean anything. Everything after that is about the same as a random guess. My guess is you're not going to get the resolution you need.

It's on an uno

This is frustrating. There has to be a way to manage epoch times, no?

I may just have to figure out how to truncate it in the raw json string before processing it. Did a quick experiment and the 10 digit epoch time (seconds) seems to be handled fine as a u-long with this code.

Hmm, is there a way I can pull it from the json as string/char * rather than a double (without adding quotes to the string)? That would simplify my life.

is there a way I can pull it from the json as string/char * rather than a double

Use the functions atol() or strtoul() to convert digits in a zero-terminated character array (c-string) to unsigned long. That way you get about 10 decimal digits.

void setup() {
Serial.begin(115200);
unsigned long x=atol("1234567890")-1;
Serial.println(x); //1234567889
}

void loop() {}

That's the opposite of what I need. The JSON library returns it from the JSON doc as a double (because it's not in quotes, and I don't control that). At that point, the "damage" is already done. What I want is for the JSON library to treat it as a c-string to begin with. THEN I can start doing something like the above.

To that end I've started writing some pre-processing to throw the quotes around the values before processing it as JSON. It's ugly but I think it will work.

The Uno does not support the double data type.

jremington:
The Uno does not support the double data type.

Sort of. You can type as a double, but it's no different than a float. The object coming back from the JSON library is typed as a "double" so I'm just calling it that for consistency. I'm aware it's just a float.

No, not even "sort of".

You get 6-7 decimal digits with a float, period, regardless of whether the AVR-gcc compiler allows you to type "double" instead of float. The decision to allow that was an incredibly bad one.

If you want to convert an ASCII string of 10 decimal digits to binary or vice versa on an Uno, your best option is unsigned long integers. That is fine for milliseconds, for up to 57 days.

Edit: Not useful for converting your example ASCII constant, though:

unsigned long x=strtoul("1559885523000",NULL,10);  //x is assigned 4294967294

There is also the arbitrary precision BigNumber library.

Which JSON library do you use ?

This is the epoch in milliseconds: https://currentmillis.com/
It is now: "1559951564946". The Arduino can not handle that, but if the three lowest digits are stripped to get the seconds, then you get "1559951564" and that is a normal 32-bit unsigned long number.

For your "1559885523000.0", remove the ".0" and remove the three lowest digits. Then convert the "1559885523" with strtoul() as jremington wrote. Then you have the epoch time.
I don't know how to use the JSON library to get that as a string. If you have that as a string, then you can strip those digits yourself before converting.

Do not try to put that into a floating point variable.

void setup() {
Serial.begin(115200);
unsigned long x=strtoul("1559885523",NULL,10);
Serial.println(x); //prints 1559885523
}

void loop() {
}

The ESP32, which can be programmed in the Arduino IDE after installing their patch, has some data that types are different than the Arduino family. It appears to support 4-byte floats and 8-byte doubles. I ran this program:

void setup() {

  float x;
  double y;
  Serial.begin(115200);
  while (Serial.available());

  Serial.print("Size of a float is ");
  Serial.println(sizeof(float));
  Serial.print("Size of a double is ");
  Serial.println(sizeof(double));
}

void loop() {

}

And it displays:

Size of a float is 4
Size of a double is 8

I ran the test on the HiLetgo ESP32-WROOM-32 board. It has over 1Mb of flash, 350Kb of SRAM, built-in Wifi and Bluetooth, and scoots along at 240MHz, all for about $15.