Philippe A.
Philippe A.

Reputation: 2955

How to initialize timespec from a double

I want to initialize a timespec structure from a double. How can I achieve accurate conversion of the fractional part to nanoseconds?

double x = 10.1;
struct timespec tp;
tp.tv_sec = (long) x;
tp.tv_nsec = (x - tp.tv_sec) * 1000000000L;
printf("%ld %ld\n", tp.tv_sec, tp.tv_nsec);
// 10 99999999  <- actual (BAD)
// 10 100000000 <- expected

That seems like a classical floating point challenge. The exact method used to extract integral and fractional does not matter (ex modf).

My current workaround is to convert the double to a string rounded to the desired precision and to extract both parts.

static const int CLOCK_PRECISION = 1;
char nanos[] = "000000000";
char str[25]; // arbitrary size suits my needs
/** Converted string length (excluding '\0'). */
int len = snprintf(str, sizeof(str), "%.*f", CLOCK_PRECISION, b);
/** Position of first decimal digit in the string. */
char *decOfs = strchr(str, '.') + 1;
/** Number of decimal digits. */
size_t decLen = len - (decOfs - str);  
memcpy(nanos, decOfs, decLen);  
a->tv_sec  = atol(str);
a->tv_nsec = atol(nanos);

I even contemplate a variant of my workaround that would init directly from a string (ex: "10.1"). This would be viable because my initialization values do not change over time.

Any solution to my initial problem? Any better ideas to initialize a timespec?

I want a scalable solution. I do not want to init from tenths or hundredths of seconds.

Upvotes: 2

Views: 7464

Answers (1)

John Zwinck
John Zwinck

Reputation: 249143

Just add this near the top:

x += 0.5e-9.

This will add half the "epsilon" (minimum value change) for the maximum precision of timespec, effectively "rounding" the input. Note that if the value of x is more than 15 decimal digits of precision, it will never be accurate, because that's all double gives you. But 10.1 should work correctly if you "round up" as above.

Upvotes: 4

Related Questions