Why doesn't left bit-shift, , for 32-bit integers work as expected when used more than 32 times?
This is caused due to a combination of an undefined behaviour in C and the fact that code generated for IA-32 processors has a 5 bit mask applied on the shift count. This means that on IA-32 processors, the range of a shift count is 0-31 only. 1
From The C programming language 2
The result is undefined if the right operand is negative, or greater than or equal to the number of bits in the left expression’s type.
From IA-32 Intel Architecture Software Developer’s Manual 3
The 8086 does not mask the shift count. However, all other IA-32 processors (starting with the Intel 286 processor) do mask the shift count to 5 bits, resulting in a maximum count of 31. This masking is done in all operating modes (including the virtual-8086 mode) to reduce the maximum execution time of the instructions.
1 http://codeyarns.com/2004/12/20/c-shift-operator-mayhem/
2 A7.8 Shift Operators, Appendix A. Reference Manual, The C Programming Language
3 SAL/SAR/SHL/SHR – Shift, Chapter 4. Instruction Set Reference, IA-32 Intel Architecture Software Developer’s Manual
Left shift an integer by 32 bits
§5.8 Shift operators
The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.
The value of
E1 << E2
isE1
left-shiftedE2
bit positions; vacated bits are zero-filled. IfE1
has an unsigned type, the value of the result isE1 × 2E2
, reduced modulo one more than the maximum value representable in the result type. Otherwise, ifE1
has a signed type and non-negative value, andE1×2E2
is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
How to shift 32 bit int by 32 (yet again)
In Java it's guaranteed to work if those variables are of type int
, since >>
in Java does an arithmetic right shift and shifting more than 31 also has defined behavior. But beware of operator precedence
int lshift(int x, int n)
{
return (x << n) & ((n-32) >> 5);
}
This will work for shift count up to 32. But it can be modified to include any int values with shift counts larger than 31 return 0
return (x << n) & ((n-32) >> 31);
However in C and C++ the size of int
type and the behavior of >>
operator is implementation defined. Most (if not all) modern implementations implement it as arithmetic shift for signed types though. Besides, the behavior of shifting more than the variable width is undefined. Worse yet, signed overflow invokes UB so even a left shift by 31 is also UB (until C++14). Therefore to get a well defined output you need to
- Use a unsigned fixed-width type like
uint32_t
(sox << 31
isn't UB) - Use a compiler that emits arithmetic right shift instruction for
>>
and use a signed type forn
, or implement the arithmetic shift yourself - Mask the shift amount to limit it to 5 bits for
int32_t
The result would be
uint32_t lshift(uint32_t x, int32_t n)
{
return (x << (n & 0x1F)) & ((n-32) >> 31);
}
If the architecture supports conditional instructions like x86 or ARM then the following way may be faster
return n < 32 ? x << n : 0;
On a 64-bit platform you can made this even simpler by shifting in a 64-bit type and then mask. Some 32-bit platforms like ARM does support shifting by 32 so this method is also efficient
return ((uint64_t)x << (n & 0x3F)) & 0xFFFFFFFFU;
You can see the output assembly here. I don't see how it can be improved further
Why doesn't left bit shift shift beyond 31 for long int datatype?
Although your x
is of type long int
, the 1
is not. 1
is an int
, so 1<<63
is indeed undefined.
Try (static_cast<long int>(1) << 63)
, or 1L << 63
as suggested by Wojtek.
Bit shifting an int 32 times in C
It's undefined behavior
Why doesn't left bit-shift, "<<", for 32-bit integers work as expected when used more than 32 times?
which means you should avoid it like the plague. Here's some links including "What every C programmer should know about undefined behavior": http://lwn.net/Articles/511767/
How do I bit shift a long by more than 32 bits?
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.
C strange behaviour when bit-shifting
Assuming int and unsigned int are 32 bit wide, the integer constant 0xffffffff
is of type unsigned int.
Right shifting an integer with a value of greater or equal the width of the integer will result in undefined behavior1.
This happens in both cases in your example.
Update:
Undefined behavior means that anything can happen. Getting the value 0xffffffff instead of 0 fits this behavior.
You cannot shift by the width of the integer type, because it says so in the standard. You must make a check if the value is greater or equal the width of the type your working with.
1 (Quoted from: ISO/IEC 9899:201x 6.5.7 Bitwise shift operators 3)
If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
Why use only the lower five bits of the shift operand when shifting a 32-bit value? (e.g. (UInt32)1 33 == 2)
It basically boils down to the way the x86 handles the arithmetic shift opcodes: it only uses the bottom 5 bits of the shift count. See the 80386 programming guide, for example. In C/C++, it's technically undefined behavior to do a bit shift by more than 31 bits (for a 32-bit integer), going with the C philosophy of "you don't pay for what you don't need". From section 6.5.7, paragraph 3 of the C99 standard:
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is
greater than or equal to the width of the promoted left operand, the behavior is undefined.
This allows compilers to omit a single shift instruction on x86 for shifts. 64-bit shifts cannot be done in one instruction on x86. They use the SHLD/SHRD instructions plus some additional logic. On x86_64, 64-bit shifts can be done in one instruction.
For example, gcc 3.4.4 emits the following assembly for a 64-bit left-shift by an arbitrary amount (compiled with -O3 -fomit-frame-pointer
):
uint64_t lshift(uint64_t x, int r)
{
return x << r;
}
_lshift:
movl 12(%esp), %ecx
movl 4(%esp), %eax
movl 8(%esp), %edx
shldl %cl,%eax, %edx
sall %cl, %eax
testb $32, %cl
je L5
movl %eax, %edx
xorl %eax, %eax
L5:
ret
Now, I'm not very familiar with C#, but I'm guessing it has a similar philosophy -- design the language to allow it to be implemented as efficiently as possible. By specifying that shift operations only use the bottom 5/6 bits of the shift count, it permits the JIT compiler to compile the shifts as optimally as possible. 32-bit shifts, as well as 64-bit shifts on 64-bit systems, can get JIT compiled into a single opcode.
If C# were ported to a platform that had different behavior for its native shift opcodes, then this would actually incur an extra performance hit -- the JIT compiler would have to ensure that the standard is respected, so it would have to add extra logic to make sure only the bottom 5/6 bits of the shift count were used.
Related Topics
When Does a Process Get Sigabrt (Signal 6)
How Much Is Too Much With C++11 Auto Keyword
Modern Way to Set Compiler Flags in Cross-Platform Cmake Project
Null VS Nullptr (Why Was It Replaced)
Make_Unique and Perfect Forwarding
Detecting Signed Overflow in C/C++
Initialize Static Variables in C++ Class
When to Use Inline Function and When Not to Use It
How to Get Main Window Handle from Process Id
Is "For(;;)" Faster Than "While (True)"? If Not, Why Do People Use It
Changing the Delimiter For Cin (C++)
Variable Length Array (Vla) in C++ Compilers
What Is a Null-Terminated String
Why Does the C++ Map Type Argument Require an Empty Constructor When Using []