5

Why does this assigning produce a comile error: Constant value '-2147483648' cannot be converted to a 'ulong' and I have to use unchecked (...) for this case?

ulong xDummy30 = (1 << 30); // works
ulong xDummy31 = (1 << 31); // ERROR 25 Constant value '-2147483648' cannot be converted to a 'ulong'
ulong xDummy32 = (1 << 32); // works

Using this instead works:

ulong xDummy31a = unchecked((ulong)(1 << 31));
// or
ulong xDummy31b = (1ul << 31); //  comment from Regis Portalez

Edit
The Question Why do I have to cast 0 (zero) when doing bitwise operations in C#? has a similar answer and the reason for the observed behaviour is the same. But they are different questions.

marsh-wiggle
  • 2,508
  • 3
  • 35
  • 52

3 Answers3

8

According to MSDN ulong reference all your integer literals 1, 30, 31 are regarded as int:

When an integer literal has no suffix, its type is the first of these types in which its value can be represented: int, uint, long,

According to MSDN << operator the result of the << operation is also an int. When yo shift by 30 the result is positive, when shifting by 31 the result is a negative int which can't be assigned to an ulong.

Edit: HVD pointed me an error in the following. Thanks HVD!

Start Error - When shifting 32 bits, the compiler knows you want an ulong, and thus the result of the shift operation is a positive long, which can be converted to an unlong - end error

The correct reason why 1<<32 does not lead to compiler error is in the provided link to operator <<:

If the first operand is an int, the shift count is given by the low-order five bits of the second operand. That is, the actual shift count is 0 to 31 bits.

32 to binary: 0010 0000; low order five bits: 0 0000, So the actual performed shift is 1 << 0, which results to the int with the value 1, which of course can be assigned to an ulong.

To solve this, make sure that your number 1 is a long. In that case 1<<31 is still a positive long.

You can also use suffixes to specify the type of the literal according to the following rules: If you use L, the type of the literal integer will be either long or ulong according to its size.

So 1L is a long; 1L <<31 is a positive long, and thus can be assigned to an ulong

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • 1
    "When shifting 32 bits, the compiler knows you want an ulong" -- Eh? That's not how it works. `1 << 32` has type `int`, and a value of `1`, as required by the C# language specification which dictates that only the bottom 5 bits of the count are used if the LHS has type `int` or `uint`. This is also mentioned in the documentation you link to. –  May 02 '16 at 07:05
1

As a supplement to my question and the accepted answer here is a note:

Quoting first the example of my question:

ulong xDummy30 = (1 << 30); // works
ulong xDummy31 = (1 << 31); // ERROR 25 Constant value '-2147483648' cannot be converted to a 'ulong'
ulong xDummy32 = (1 << 32); // works

It is correct that this assignement brings no compile error:

ulong xDummy32 = (1 << 32);

but it does not "work", contrary to what I wrote in my question. After this the result of xDummy32 is not 4294967296 but it is 1. Therefore bit shifts > 30 must be written this way:

ulong xDummy32 = (1UL << 32);
marsh-wiggle
  • 2,508
  • 3
  • 35
  • 52
0
    var a = (1<<31);
    Console.WriteLine(a);

    ulong num = unchecked((ulong)(1<<31));
    Console.WriteLine(num);

Output:
-2147483648
18446744071562067968

1<<31 value is unable to fit into uInt64

https://dotnetfiddle.net/Widget/TfjnSZ

BetterLateThanNever
  • 886
  • 1
  • 9
  • 31