Why can't I assign (1<<31) to an ulong var? (Error 25 Constant value ... cannot be converted ...)
Asked Answered
U

3

7

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.

Uziel answered 2/5, 2016 at 6:27 Comment(4)
1<<31 might be evaluated with int32 operands. Can you evaluate 1ul<<31?Disillusion
@RegisPortalez works (like the unchecked(...) version). Is there any reason for that?Uziel
@boboes this is just the default way C# treats simple numbers.Joella
Possible duplicate of Why do I have to cast 0 (zero) when doing bitwise operations in C#?Facture
N
9

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

Norrie answered 2/5, 2016 at 6:59 Comment(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.Drollery
U
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);
Uziel answered 17/6, 2019 at 17:26 Comment(0)
D
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

Dedra answered 2/5, 2016 at 6:44 Comment(3)
Range of ulong is 0 to 18,446,744,073,709,551,615Dedra
Why should 2^31 be unable to fit into an UInt64? The range of UInt64 is [0, 2^64-1]. You just have to use correct datatypes. var e = 1L << 62; works perfectly.Jordanson
"fit" is the wrong word. I believe you are looking for "convert".Corlisscorly

© 2022 - 2024 — McMap. All rights reserved.