简体   繁体   中英

C++: converting __int64 to unsigned long

I'd like to use the following function in windows and linux, but I am not sure how to convert an __int64 to unsigned long. Is it safe to cast the value like I did?

getTimeInMilliseconds()
{
    #ifdef _WIN32


        static const __int64 magic = 116444736000000000; // 1970/1/1
        SYSTEMTIME st;
        GetSystemTime(&st);
        FILETIME   ft;
        SystemTimeToFileTime(&st,&ft); // in 100-nanosecs...
        __int64 t;
        memcpy(&t,&ft,sizeof t);
        return (unsigned long)((t - magic)/10000);
    #else
        struct timeval tv;
        gettimeofday(&tv, NULL);
        unsigned long s = tv.tv_sec * 1000;
        unsigned long us = tv.tv_usec / 1000;
        return s + us;
    #endif
}

Yes it's "safe" in the sense that you can do, syntactically, what's written in the code. When downgrading a 64 bit int to a 32 bit long int, you naturally lose the top 32 bits, which isn't a problem if your value is small enough, the problem is that your value is not in fact small enough.

both definitions (WIN32 and not) are subject to overflow problems, not because you can't cast a 64 bit int to a long, but because you are trying to cram too large of a value into too small of a container.

Problem #1, the function name describes the result as "time in milliseconds" which is false, both implementations are attempting to return the result in microseconds (thousands of a second) not "milliseconds" which is millionths of a second. Decide what result you're actually shooting for and label it appropriately, reduce confusion in the long run. Note that the 100-nanosecond (billionth of a second) intervals is the same as 1/10th of a millisecond, which is 1/10,000th of a microsecond, just for reference.

Problem #2: The "memcpy" to get the FILETIME struct into a long int is also wrong. FILETIME and __int64 have different byte alignments, and you'll get the bits in wrong places that way. To turn a FILETIME into an _int64, you need to do this:

LARGE_INTEGER foo;
FILETIME baz;
__int64 bar;

foo.HighPart = baz.dwHighDateTime;
foo.LowPart = baz.dwLowDateTime;
bar = foo.Quadpart;

Problem #3: a 32 bit long int can only store time values large enough to be useful in seconds and even then only until early in the year 2038, that's why "timeval" has a separate member for 'microseconds' it wasn't just to make your life hard.

If you want thousands of a second in a single value, you need at least 36 bits, probably more like 38-40 to have times that can refer to values in more robust time frame than 1970-2038. Since very few compilers have support for 40 bit integers, I would recommend just upgrading to a full 64 bits, unless for some reason you can't in which case you should come up with a better solution for what you're trying to do all around.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM