tags:

views:

452

answers:

6

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

+8  A: 

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.

AndreyT
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
@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.
AndreyT
+14  A: 

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
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?
mach7
Cthutu
@Cthutu: 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.
AndreyT
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
+6  A: 

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
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
A: 

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
A: 

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
A: 

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