tags:

views:

167

answers:

4

I am using a library that returns a structure with a time stamp that is represented by two (TimestampHi, TimestampLo) unsigned longs. I pretty much only need the timestamp printed out as %llu in printf.

What's the easiest way to get the data from these two ints and correctly use it as a uint64_t?

+9  A: 

Assuming that unsigned long long is a 64-bit type on your platform

assert(sizeof(unsigned long) * CHAR_BIT == 32);
assert(sizeof(unsigned long long) * CHAR_BIT == 64);
// Static asserts are more appropriate in cases like this

unsigned long long Timestamp = TimestampHi;
Timestamp <<= 32; Timestamp += TimestampLo; 

And then print the Timestamp value.

Same thing as an one-liner

unsigned long long Timestamp = ((unsigned long long) TimestampHi << 32) + TimestampLo; 

or simply

printf("%llu\n", ((unsigned long long) TimestampHi << 32) + TimestampLo);

If you wish to abstract your code from bit-based operations, the expression can be rewritten as

TimestampHi * ((unsigned long long) ULONG_MAX + 1) + TimestampLo
AndreyT
I'd probably not bother with the `*CHAR_BIT`, but +1 for excruciating correctness. (Who has non-8-bit bytes anymore?)
Zack
@Zack: Various DSPs have 16- or 32-bit bytes. @AndreyT: is `unsigned long long` guaranteed not to have padding bits?
Steve Jessop
@Steve Jessop: Neither `unsigned long`, nor `unsigned long long` guaranteed not to have padding bits. So, of course, my assertions are not really checking what there were supposed to check. The above is written under assumption that they have no padding bits and/or that `unsigned long long` is sufficiently large to store a product of two arbitrary `unsigned long` values.
AndreyT
-1 for DSP pedantry when the OP specifically said 32-bit and 64-bit types. By the way, C99 requires `ULONG_MAX` to be at least 2^32-1 and `ULLONG_MAX` to be at least 2^64-1. So these types can be used without any ridiculous asserts to store 32-bit and 64-bit numbers (of course they may be bigger than needed). If you care, use `uint32_t` and `uint64_t`, or if you're really pedantic and worried they might not exist, use `uint_least32_t` and `uint_least64_t`.
R..
If the platform really would not be standard conformant (C99 is not adopted everywhere, it seems) *and* your are extremely cautious about padding bits, going through `sizeof` in the assertions is probably the wrong way. Probably you should check if `(unsigned long)-1` and `(unsigned long long)-1` are `>=` to 2^32-1 and 2^64-1.
Jens Gustedt
`TimestampHi << 32ull` does *not* cause `TimestampHi` to be promoted to `unsigned long long`. The shift operators are a little different in this regard - the operands are promoted individually, and the type of the result is that of the promoted left operand (which in this case, will be unchanged). You need to use `(unsigned long long)TimestampHi << 32`.
caf
@R.: To be fair to AndreyT, he provides an assertion which, if it passes, ensures that the OP's assumption is true: long exactly 32 bits, long long exactly 64. It's wise to check such platform-assumptions, where possible. AndreyT's test has no false positives on conforming implementations - `assert(sizeof(long) == 4)` could have false positives (e.g. if `long` is 64 bit and `char` is 16-bit), so the use of CHAR_BIT is an improvement. Unfortunately the test could give false negatives where there are padding bits (e.g. 32-bit `long`, 9-bit `char`), as AndreyT acknowledges. I don't see a problem.
Steve Jessop
@caf: You are right. Thanks for the correction. I had my doubts (since `<<` is an "asymmetrical" operation), but forgot to check it.
AndreyT
+1  A: 

Andrey answered what you do if you have some 64-bit type, but maybe you have only 32-bit types. In that case, what you need is a multi-precision integer library. The GNU MP library is excellent, but may be overkill for your purposes. I8lib implements "double" precision integer arithmetic (i.e. no more than 64 bits) and might do you fine, but i've never used it.

Zack
A compiler without a 64-bit type is not conformant (to the current C standard).
R..
@R: a compiler without a 64-bit type is common in embedded programming
tomlogic
A: 

If the answer will be less than 2^49, and 64-bit ints aren't available, how about printf("%1.0f",4294967296.0*upper_part + lower_part)? That wouldn't work on platforms where a double is less than 64 bits, but it would work on many platforms without 64-bit ints.

supercat
Why not separate the printing of the 2 parts using 2 calls to `sprintf`, then add them with ASCII addition? That way you just need 32 bits of precision in the mantissa. On the other hand, it's possible that your system's `printf` sucks and just prints garbage (e.g. zeros) in the low places after it's printed enough digits to uniquely determine the number. (See my question http://stackoverflow.com/questions/3215235/how-do-you-print-the-exact-value-of-a-floating-point-number)
R..
+3  A: 
unsigned long long t = (unsigned long long)hi << 32 | lo;
printf("%ull\n", t);

Note that I'm using unsigned long long instead of uint64_t because it's

  1. guaranteed to be at least 64 bits, and
  2. easy to print with printf - you don't need the PRIu64 macro, and
  3. you mentioned %ull in the question.

However, if you want to support MSVC (which is nowhere near conformant to modern standards), you might be better off with:

uint64_t t = (uint64_t)hi << 32 | lo;
printf("%" PRIu64 "\n", t);

This way you can ensure (via replacements for the missing system headers) that uint64_t is properly defined as MSVC's 64-bit unsigned type, and PRIu64 is defined as the proper printf format specifier to print it.

R..
+1 for a portable solution (for platforms that support a 64-bit int).
tomlogic