Question

I know, that it is well defined by the C standard that (unsigned)-1 must yield 2^n-1, i. e. an unsigned integer with all its bits set. The same goes for (uint64_t)-1ll. However, I cannot find something in the C11 standard that specifies how (uint64_t)-1 is interpreted.

So, the question is: Is there any guarantee in the C standard, which of the following holds true?

(uint64_t)-1 == (uint64_t)(unsigned)-1   //0x00000000ffffffff
(uint64_t)-1 == (uint64_t)(int64_t)-1    //0xffffffffffffffff
Was it helpful?

Solution

Yes. See C11 6.3.1.3 Signed and unsigned integers:

1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.60)

3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

60) The rules describe arithmetic on the mathematical value, not the value of a given type of expression.

Case 2 applies, so -1 is reduced modulo 0x10000000000000000 to yield 0xffffffffffffffff.

OTHER TIPS

The expressions 1 and -1 have type int. When converted to uint64_t, the principle that 2n is added or subtracted until the value is in range applies, so the result is always 2n-1, in this case with n=64. Therefore, (uint64_t)-1 is always 264-1..

The expression (int64_t)-1 evaluates to -1, so the same reasoning applies to the expression (uint64_t)(int64_t)-1, which too always evaluates to 264-1.

On the other hand, (unsigned)-1 is a positive value of type unsigned int that may be 216-1, 232-1, 264-1 or various other values depending on the compilation platform. These values may not yield 264-1 when converted to uint64_t.

I'm guessing you're writing (uint64_t)-1 instead of -1ULL because you don't want to make assumptions about the size of unsigned long long? If so, that's good. However, there is an alternative which hasn't been mentioned yet (and doesn't actually answer your question) but can save a lot of worry by side-stepping the question:

An alternative

A good habit to be in is to always use UINT64_C(x) instead of (uint64_t)x. This is a macro defined in <stdint.h> which automatically appends U, UL, or ULL as needed. Thus, UINT64_C(-1) resolves to either -1U, -1UL, or -1ULL, depending on your target. This is guaranteed to always work correctly.

Perils of type-casting

Note that (uint64_t)x actually does not even work correctly in general. For example,

(uint64_t)2147483648                // RISKY

generates a warning on some compilers because the value 2147483648 (2^31) is too big to fit into a 32-bit integer, and the following does not even remotely work:

(uint64_t)1000000000000000000       // RISKY

However, if you use UINT64_C() instead, then everything is golden:

UINT64_C(2147483648)                // GOOD

UINT64_C(1000000000000000000)       // GOOD

UINT64_C(-1)                        // GOOD

Notes:

  • The _C suffix stands for “constant.”
  • In <stdint.h> there are also 8-, 16-, and 32-bit versions for both signed and unsigned values.
  • For the special case of –1, you could also just write UINT64_MAX.

This is a question that can be answered with a few lines of code.

#include <stdio.h>

main()
{
    int          x;
    unsigned int y;

    x = -1;
    printf("\n0x%08x", x);

    y = (unsigned int) -1;
    printf("\n0x%08x", y);

}

Running this code on Eclipse/Microsoft C compiler produces:

0xffffffff
0xffffffff

A similar program can show you what uint64_t produces.

Finally, if you understand how computers use 2's complement numbers to add numbers, then you will understand that -1 for words of any number of bits (8, 32, 64, etc.) is always all ff's for every byte in the word/double word, etc.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top