0

Here is the program whose compilation output makes me cry:

#include <inttypes.h>

int main()
{
        uint32_t limit = (1 << 32) - 1; // 2^32 - 1 right?
}

and here is the compilation output:

~/workspace/CCode$ gcc uint32.c 
uint32.c: In function ‘main’:
uint32.c:5:29: warning: left shift count >= width of type [-Wshift-count-overflow]
         uint32_t limit = (1 << 32) - 1; // 2^32 - 1 right?

I thought that (1 << 32) - 1 equals to 2^32 - 1 and that unsigned integers on 32 bits range from 0 to 2^32 - 1, isnt it the case? Where did I go wrong?

Simonlbc
  • 591
  • 1
  • 4
  • 16
  • The warning says it all. You cannot compute the value of an expression that cannot be expressed. – Kerrek SB May 25 '16 at 22:53
  • Perhaps the issue here is that (1 << 32) requires 1 bit more of space (33 bits) to store the value -- so the compiler is having fits since you are working with a 32 bit variable. – Nicholas Patton May 25 '16 at 22:55
  • 1
    with 32-bit int, 2^32 - 1 will fit (unsigned)... problem is, 2^32 won't, and it's a part of the expression you calculate 2^32-1 with. However, for other reasons, you'll likely get the right answer in the end anyway since uint32_t will be modulo 2^32. – Dmitri May 25 '16 at 22:55
  • Try using `(1ULL << 32)`. – jxh May 25 '16 at 22:56
  • 2
    Why not just `0xFFFFFFFF`? – Dan Mašek May 25 '16 at 23:00
  • 1
    @jxh 1ULL << 32 seems to make the compiler happy! What is 1ULL? – Simonlbc May 25 '16 at 23:00
  • 1
    The `ULL` suffix means the constant is of the `unsigned long long`. – Keith Thompson May 25 '16 at 23:09
  • 1
    @Simonlbc it's an `unsigned long long` literal, and is at least 64 bits wide. – Ian Abbott May 25 '16 at 23:09
  • Just because the result is supposed to fit into the range of the target type does not mean that you should not care about the intermediate values fitting into that range. Even if we ignore other errors, arriving at 2^32-1 (which is in range) requires you to pass through 2^32 (which is out of range). – AnT stands with Russia May 25 '16 at 23:18

3 Answers3

5

The warning is correct, the highest bit in a 32bit number is the 31st bit (0 indexed) so the largest shift before overflow is 1 << 30 (30 because of the sign bit). Even though you are doing -1 at some point the result of 1 << 32 must be stored and it will be stored in an int (which in this case happens to be 32 bits). Hence you get the warning.

If you really need to get the max of the 32 bit unsigned int you should do it the neat way:

#include <stdint.h>

uint32_t limit = UINT32_MAX;

Or better yet, use the c++ limits header:

#include <limits>
auto limit = std::numeric_limits<uint32_t>::max();
Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175
3

You have two errors:

  • 1 is of type int, so you are computing the initial value as an int, not as a uint32_t.
  • As the warning says, shift operators must have their shift argument be less than the width of the type. 1 << 32 is undefined behavior if int is 32 bits or less. (uint32_t)1 << 32 would be undefined as well.

(also, note that 1 << 31 would be undefined behavior as well, if int is 32 bits, because of overflow)

Since arithmetic is done modulo 2^32 anyways, an easier way to do this is just

uint32_t x = -1;
uint32_t y = (uint32_t)0 - 1; // this way avoids compiler warnings
  • That looks like C++ to me! – Ian Abbott May 25 '16 at 23:01
  • is the 1 in -1 kind of converted to uint32_t? How come it works well? – Simonlbc May 25 '16 at 23:11
  • 1
    @Simonlbc: The integer promotion rules guarantee the calculation will be performed either in a signed integral type large enough to represent every element of `uint32_t`, or if there is no such thing, in `uint32_t` itself. Either way, the end result is a well-defined number equivalent to `-1` modulo `2^32`. There's no harm in casting `1` to `uint32_t` as well, if you want. –  May 25 '16 at 23:16
  • 1
    @Simonlbc it uses modulo 2^32 arithmetic. You can also just do `uint32_t x = -1;`, which is perfectly legal (assuming `uint32_t` is implemented), although some compilers might issue a warning anyway. – Ian Abbott May 25 '16 at 23:16
  • Right; I do `0-1` to reassure the compiler; you *can* just do `-1` directly if you're willing to put up with the warning or decide not to enable that warning... but I prefer to enable warnings and have warning-free builds. –  May 25 '16 at 23:18
1

The compiler is using int internally in your example when trying to calculate the target constant. Imagine that rhe compiler didn't have any optimization available and was to generate assembler for your shift. The number 32 would be to big for the 32bit int shift instruction.

Also, if you want all bits set, use ~0

Stian Skjelstad
  • 2,277
  • 1
  • 9
  • 19