ctimetimespec

In struct timespec tv_sec and tv_nsec express the same time in seconds and in nano-seconds or they have to be sum for obtaining the time?


double timespec_delta2milliseconds(struct timespec *last, struct timespec *previous)
{
    
 return (last->tv_sec - previous->tv_sec) + (last->tv_nsec - previous->tv_nsec)*pow(10,-3); 

}

This function computes the difference (last - previous) and returns the result expressed in milliseconds as a double. I tried a lot of different ways but if I don't do like this i receve in output segmentation fault. I think that this solution works but it's wrong, someone can help me ?


Solution

  • You need two functions: sub_timespec(), which calculates the difference between two time spec values, and timespec_as_milliseconds(), which returns the number of milliseconds in a time spec value as an integer.

    enum { NS_PER_SECOND = 1000000000 };
    
    void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td)
    {
        td->tv_nsec = t2.tv_nsec - t1.tv_nsec;
        td->tv_sec  = t2.tv_sec - t1.tv_sec;
        if (td->tv_sec > 0 && td->tv_nsec < 0)
        {
            td->tv_nsec += NS_PER_SECOND;
            td->tv_sec--;
        }
        else if (td->tv_sec < 0 && td->tv_nsec > 0)
        {
            td->tv_nsec -= NS_PER_SECOND;
            td->tv_sec++;
        }
    }
    
    int64_t timespec_as_milliseconds(struct timespec ts)
    {
        int64_t rv = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
        return rv;
    }
    

    If you want to round the milliseconds, it gets trickier because you have to worry about carries and negative numbers and so on. You should not encounter a timespec value where the tv_sec and tv_nsec values have opposite signs (zeros aren't a problem).

    In your code, adding pow(10, -3) mixes floating point arithmetic with integer arithmetic — usually not a good idea.

    If you want a double value with up to 3 decimal places of fractional seconds, then you need:

    double timespec_to_double_milliseconds(struct timespec ts)
    {
        double rv = ts.tv_sec + (ts.tv_nsec / 1000000) / 1000.0;
        return rv;
    }
    

    The first division is (deliberately) integer division; the second gives a floating-point value. Again, rounding has problems with carrying and so on.

    Your function then becomes:

    double timespec_delta2milliseconds(struct timespec *last, struct timespec *previous)
    {
        struct timespec delta = sub_timespec(*last, *previous);
        return timespec_to_double_milliseconds(delta); 
    }
    

    You can use an extra value in the function so it is easier to print the value returned in a debugger: double rv = timespec_to_double_milliseconds(delta); return rv;.

    The key idea, though, is to do separate tasks in separate functions. Taking the difference between two struct timespec values is one task; converting a struct timespec value to an appropriate double is a separate task. When you can split things into separate tasks, you should.

    I often pass struct timespec values by value rather than pointer. The structure size is typically small enough that it is not a stress on the stack or registers. I return them by value too, which simplifies memory management — YMMV.

    And, just in case it isn't clear, the tv_sec member of a struct timespec contains an integer number of seconds, and the tv_nsec contains the fractional part of a second expressed as a number of nanoseconds (0 to 999,999,999). It requires care in printing the tv_nsec value; you need a format such as %.9ld to print 9 digits with leading zeros, and the type is long. To print microseconds, divide the tv_nsec value by 1,000 and change 9 to 6; to print milliseconds, divide by 1,000,000 and change 9 to 3. Beware negative values!