2

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.

Philippe A.
  • 2,885
  • 2
  • 28
  • 37

1 Answers1

4

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.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • +1. Might want expand per OP's "I want a scalable solution". – chux - Reinstate Monica Aug 15 '14 at 15:22
  • Looks scalable enough to me. By scalable I mean I don't want to convert my init values from one base to another if the need for greater precision arises (say converting tenths to hundredths, and then hundredth to whatever). Besides, the max theoritical timer precision is 1 nano. I will never need that. It is already more than sufficient if the solution works up to microseconds. – Philippe A. Aug 15 '14 at 15:31