27

It seems like I should be able to perform bit shift in C/C++ by more than 32 bits provided the left operand of the shift is a long. But this doesn't seem to work, at least with the g++ compiler.

Example:

unsigned long A = (1L << 37)

gives

A = 0

which isn't what I want. Am I missing something or is this just not possible?

-J

7 Answers7

29

A is equal to 0 because A only has 32-bits, so of course you are shifting all the bits off to the left leaving only 0 bits left. You need to make A 64-bit:

unsigned long long A = (1ULL << 37);

Or if you intend to use Visual C++:

unsigned __int64 A = (1ULL << 37);
Cthutu
  • 8,713
  • 7
  • 33
  • 49
  • 1
    I guess I'm used to Java where a long is guaranteed to be 64 bits. If it's only 32 bits, how is it different from an int? –  Mar 08 '10 at 20:28
  • 3
    To make things a bit clearer I would like to clarify something. The sizes of C types char, short, int, long, long long vary depending on machine architecture and compiler. However, the C standard guarantees this relationship: sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long) But for typical 32-bit machines, which is probably what you're compiling on, the sizes for char, short, int, long and long long are 8, 16, 32, 32 & 64 bits respectively. – Cthutu Mar 08 '10 at 20:28
  • 6
    A pedantic remark: C standard does not guarantee that. C++ standard does. C standard says that `range of char <= range of short <= range of int <= range of long <= range of long long`, but it says nothing about the `sizeof`. Formally, the relationship for `sizeof` might not hold in C, although that would be a very strange and exotic thing to see. – AnT stands with Russia Mar 08 '10 at 20:31
  • 2
    Additionally, `int` is guaranteed to be at least 16 bits, `long` at least 32 bits, and `long long` at least 64 bits. These are actually specified in terms of minimum ranges. – caf Mar 08 '10 at 22:00
  • 1
    I will add that I think the latest Microsoft C++ compiler supports "long long" now. – Cthutu Nov 07 '13 at 21:07
15

Re-try this using a variable of type uint64_t (from stdint.h) instead of long. uint64_t is guaranteed to be 64 bits long and should behave as you expect.

bta
  • 43,959
  • 6
  • 69
  • 99
  • 5
    If `long` is 32 bits then `(1L << 37)` will be zero regardless of the type of the variable. `1` has to be cast to the destination type as well. – Potatoswatter Mar 09 '10 at 03:39
  • 9
    @Potatoswatter: If `long` is 32 bits then the behavior is **undefined** for shifts of 32 bits or more. In fact, on GCC/x86, `1L << 37 == 32`. Same for GCC/PowerPC. (What's happening is the shifter just gets the low five bits.) – Dietrich Epp Jul 11 '11 at 22:34
  • 3
    @Dietrich: True. My point (which I also apparently neglected to state) is that the expression he probably wanted is `1LL << 37`. – Potatoswatter Jul 12 '11 at 22:29
  • I am using `long long int` and still I am getting wrong. – Prvt_Yadav Jul 22 '19 at 13:23
  • 1
    @Prvt_Yadv Make sure the size of `long long int` on your platform is larger than the size of your shift operation. Does it work if you use type `uint64_t` instead? Also, take heed of Cthutu's answer below. – bta Jul 22 '19 at 22:07
  • Oh I forgot to typecast 1 in `x=1< – Prvt_Yadav Jul 23 '19 at 03:30
11

Well, it depends on the actual size of the type long (more precisely, its width in bits). Most likely on your platform long has width of 32 bits, so you get 0 as the result (also, see P.S. below). Use a bigger type. long long maybe?

P.S. As an additional note, shifting a type by more bits than its width (or equal number of bits) produces undefined behavior in C and C++ (C++ uses the term length instead of width). So, you are not guaranteed to get 0 from neither 1L << 37 nor 1L << 32 on a platform where long has width 32.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Right, the rationale would be that in some CPUs the shift instruction will only look at the low bits and the C compiler is allowed to duplicate this behavior. So (i << 37) on a 32-bit int might be the same as (i << 5). Course "undefined behavior" means it could do absolutely anything, but this is likely to be the other "observed" behavior besides returning 0. – Ben Voigt Mar 09 '10 at 01:04
  • 1
    @Ben Voigt: Exactly. Moreover, this is one of the examples of UB that actually manifests itself in practice. On such hardware one might often notice that, for example, `int i = 5; i <<= 37;` produced different result from `int i = (5 << 37);`, since the former was calculated by the CPU at run-time and the latter - by the compiler at compile-time. – AnT stands with Russia Mar 09 '10 at 04:59
  • +1 for the P.S. I just ran a piece of code where right-shifting a 32 bits unsigned by 32 bits results in the same 32 bits unsigned. – PinkTurtle Sep 11 '18 at 07:47
3

Are you sure that a long is 64 bits with your particular OS and compiler ? Use stdint.h and try it like this:

#include <stdint.h>

uint64_t x = (1ULL << 37);
Paul R
  • 208,748
  • 37
  • 389
  • 560
0

New C++ Standard introduces LL and ULL suffixes for integer literals. You could try to use them since all latest compilers supports them. But you should know that it is not part of current C++ Standard.

long long A = (1LL << 37)
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
0

In your case you're restricted to the language base types. A generic solution for [quasi] arbitrary size integers is using Integer class under Crypto++.

sw.
  • 3,240
  • 2
  • 33
  • 43
-2

You have the arguments reversed.

unsigned long A1 = (1L << 37); // is 2 to the 37th 
unsigned long A = (37UL<<1UL); // has just multiplied 37 by 2
Shaishav Jogani
  • 2,111
  • 3
  • 23
  • 33