I want to convert a very long string of numbers to a double in a portable way in C. In my case, portable means that it would work in Linux and Windows. My ultimate goal is to be able to pack a string of numbers into an 8-byte double and fwrite/fread to/from a binary file. The number is always unsigned.
I am using this string to pack a 4 digit year, 2 digit month, 2 digit day, 4 digit HH:MM, 1 digit variable, and a 10 digit value. So, trying to pack 23 bytes into 8 bytes.
I have tried all of the standard things:
char myNumAsString[] = "1234567890123456789";
char *ptr;
char dNumString[64];
double dNum;
dNum = atol(myNumAsString);
sprintf(dNumString, "%lf", dNum);
dNum = atof(myNumAsString);
sprintf(dNumString, "%lf", dNum);
dNum = strtod(myNumAsString, &ptr);
sprintf(dNumString, "%lf", dNum);
sscanf(myNumAsString, "%lf", &dNum);
sprintf(dNumString, "%lf", dNum);
And none of these work; they all round off the last several numbers. Any portable way to do this?
Take advantage that part of the string is a timestamp and not any set of digits.
With 60 minutes, 24 hours, 365.25 days/year, y
years, a digit and 10 digits, there are 60*24*365.25*y*10*pow(10,10)
combinations or about 5.3e16 * y
An 8-byte, 64-bit number has 1.8e19
combinations. So if the range of years is 350 or less (like 1970 to 2320), things will fit.
Assuming unix timestamp, and OP can convert a time string to time_t
(check out mktime()
) ....
time_t epoch = 0; // Jan 1, 1970, Adjust as needed.
uint64_t pack(time_t t, int digit1, unsigned long long digit10) {
uint64_t pack = digit1 * 10000000000 + digit10;
time_t tminutes = (t - epoch)/60;
pack += tminutes*100000000000;
return pack;
}
Reverse to unpack.
Or a more complete portable packing (code untested)
#include <time.h>
// pack 19 digit string
// "YYYYMMDDHHmm11234567890"
uint64_t pack(const char *s) {
struct tm tm0 = {0};
tm0.tm_year = 1970 - 1900;
tm0.tm_mon = 1-1;
tm0.tm_mday = 1;
tm0.tm_isdst = -1;
time_t t0 = mktime(&tm0); // t0 will be 0 on a Unix system
struct tm tm = {0};
char sentinal;
int digit1;
unsigned long long digit10;
if (strlen(s) != 4+2+2+2+2+1+10) return -1;
if (7 != sscanf(s, "%4d%2d%2d%2d%2d%1d%10llu%c", &tm.tm_year,
&tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min,
&digit1, &digit10, &sentinal)) return -1;
tm.tm_year -= 1900;
tm.tm_mon--;
tm.tm_isdst = -1;
time_t t = mktime(&tm);
double diff_sec = difftime(t, t0);
unsigned long long diff_min= diff_sec/60;
return diff_min * 100000000000 + digit1*10000000000ull + digit10;
}