3

I have been having some strange issues with unsigned long long.

It happens when I set an unsigned long long (I used size_t, however the problem is repeatable with u-l-l). I have set it to 2^31, however for some reason it reverts to 18446744071562067968, or 2^64 - 2^31. Keep in mind I am using an x64 compilation:

unsigned long long a = 1 << 31;
cout << a;

//Outputs 18446744071562067968, Expected 2147483648

I thought the limits of u-l-l were 2^64-1? So why can 2^31 not be stored? 2^30 works just fine. Sizeof(a) returns 8, which is 64 bits if I am not mistaken, proving the limit of 2^64-1.

I am compiling on Visual C++ 2013 Express Desktop.

My only guess is that it is some type of overflow error because it doesn't fit a normal long type.

Dude Dude
  • 33
  • 1
  • 4
  • Hint: `1<<31` is an `int` expression. Not long, not unsigned. – n. 'pronouns' m. Jun 01 '14 at 02:25
  • Thanks, that explains the limit... then is there are another way to perform binary operations on long long? – Dude Dude Jun 01 '14 at 02:31
  • 1ULL << 31, for example – rici Jun 01 '14 at 02:31
  • Or `long long n = 1; long long a = n << 31;` types like `uint64_t` from `stdint.h` guarantee the length of the integer. – James King Jun 01 '14 at 02:33
  • Wow, something so simple... sorry just haven't worked with these types to much. – Dude Dude Jun 01 '14 at 02:37
  • Note: `1 << 31` causes undefined behaviour (if int is 32-bit). Although it produces `INT_MIN` on common implementations , this is not guaranteed. – M.M Jun 01 '14 at 03:15
  • 1
    The thing to remember is that the type of an expression (including a subexpression) is generally determined by the expression itself, not by the context in which it appears. – Keith Thompson Jun 01 '14 at 04:21
  • [Type of integer literals not int by default?](https://stackoverflow.com/q/8108642/995714), [What is the default type of integral literals represented in hex or octal in C++?](https://stackoverflow.com/q/38782709/995714) – phuclv Oct 16 '18 at 10:28

1 Answers1

2

What you're seeing is sign extension when the negative integer value is assigned to the unsigned long long.

To fix it you need to make the value unsigned to begin with, something like this:

#include <iostream>
#include <iomanip>

int main()
{
    unsigned long long a = 1ull << 31ull;
    std::cout << a << "\n";
    std::cout << std::hex << a << "\n";

    return 0;
}

If you have the warning level set high enough (/W4) you'd see a warning about the signed/unsigned mismatch.

Just to be complete, you don't need to qualify both arguments, just the left operand is fine, so unsigned long long a = 1u << 31; would work. I just prefer to be as explicit as possible.

Retired Ninja
  • 4,343
  • 3
  • 21
  • 35
  • No warning in g++ with OP's original code; maybe Microsoft is different – James King Jun 01 '14 at 02:34
  • No warnings either for me... Thanks for the help though. – Dude Dude Jun 01 '14 at 02:36
  • It's a level 4 warning in Visual Studio, so it's possible you have warnings set lower. You'd need `-Wconversion` to see a warning in gcc I believe. – Retired Ninja Jun 01 '14 at 02:40
  • There is no need to apply the `ull` suffix to `31` in `1ull << 31`. Unlike most binary operators where the type of the result is determined by the type of both operands, the type of `a << b` is determined only by the type of `a` (and `int` is otherwise sufficient to represent 31). – Pascal Cuoq Jun 01 '14 at 02:45
  • Ah, that's just what I was editing in. I generally add the suffix to everything to avoid issues if things get moved around in more complex expressions. – Retired Ninja Jun 01 '14 at 02:51
  • Re. "just one is fine" not quite; the left operand must have the suffix. – M.M Jun 01 '14 at 03:12
  • @MattMcNabb Thank you. I added that. – Retired Ninja Jun 01 '14 at 05:43