3

The C standard states that a long int is at least 4 bytes - on my system it is 8 bytes.

This means I can store values up to 2^63-1 in a long and 264-1 in an unsigned long.

However, when the following code is compiled with the -Wall flag it gives the warning [Wimplicitly-unsigned-literal]:

int main (int argc, char ** argv) {

  unsigned long a;
  a = 18446744073709551615; // 2^64-1

}

If I use 263-1 (9223372036854775807) instead, it compiles with no warnings (as expected - 263-1 will fit in a signed long int).

For a project I needed to have the maximum value in an unsigned long, and I discovered that (9223372036854775807 << 1) + 1 will not raise this warning. My teacher then suggested that I could use the ULONG_MAX defined in limits.h and this gave no warnings.

Why can't I do this without a warning saying it was converted implicitly - when I declared it explicitly?

Tim
  • 2,563
  • 1
  • 23
  • 31

3 Answers3

6

Per the C standard, the type of a decimal constant without a suffix is int, long int, or long long int, specifically the first of those that is sufficient to represent the value. In your C implementation, none of those can represent 18446744073709551615, because it is too large.

In order to accommodate you, the compiler is making its type unsigned long. Technically, this does not conform to the C standard, so the compiler is warning you.

In this case, no harm is caused, because you are assigning the value to an unsigned long. But there are situations in which using the wrong type can cause problems, so generally you should append a suffix to such constants to ensure they match how they are intended to be used. In this case, a u is sufficient; as with unsuffixed types, the compiler will decide whether to use unsigned int, unsigned long int, or unsigned long long int depending on the magnitude of the number and the capabilities of the types.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
5

You declared it explicitly, but without an U, which would make it unsigned. As there is no signed integer constant with this value, it implicitly makes it unsigned, providing you with the information that you'd better make it explicit.

Do so with a = 18446744073709551615U;.

glglgl
  • 89,107
  • 13
  • 149
  • 217
  • I see - it's saying the number I've typed has been implicitly converted - not the variable I declared. – Tim Nov 09 '17 at 16:13
  • Shouldn't it be `ull`? Or at least `ul`? – Nikos C. Nov 09 '17 at 16:14
  • 2
    @NikosC.: There is no need for `ul` or `ull` in this case. The C standard says that types of decimal constants adjust somewhat to accommodate the value. If a `u` suffix is used, the type will be the first of `unsigned int`, `unsigned long int`, or `unsigned long long int` that can represent the value. – Eric Postpischil Nov 09 '17 at 16:19
  • 1
    @NikosC. Check out the promotion table for integer literals in my answer. – Petr Skocik Nov 09 '17 at 16:36
4

Check out this fancy table from the standard (6.4.4.1p5):

integer constant promotion table from the standard

which explains how integer literals are fitted into integer types.

Basically, because your literal is decimal and doesn't have a suffix, it tries to fit into the following types:

int
long int
long long int

Since it won't fit into long long int, you're getting the warning you're getting.

If you add a U (u) suffix (or UL or UL or the lowercase variants):

unsigned long a = 18446744073709551615U;

you won't have that problem, because you'll move to the promotion sequence:

unsigned int
unsigned long int
unsigned long long int

(or the appropriate subsets for UL and UL) and the literal will fit at unsigned long int (if that's indeed the first unsigned type with no less than 64 usable bits, as it usually is).

Alternatively, you can turn the warning off by switching to an octal or hexadecimal literal:

unsigned long a = 0xFFFFFFFFFFFFFFFF;
unsigned long octal_a = 01777777777777777777777;

as for these the promotion sequence is:

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

as per the linked table, which again, allows them to fit into the first 64-bit large unsigned type (usually unsigned long int).

What you should absolutely not do is:

(9223372036854775807 << 1) + 1 //don't do this!

as this invokes undefined behavior by breaking 6.5.7p4 regardless of whether a warning is generated or not.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142