Weird Behavior of Right Shift Operator (1 >> 32)

Weird behavior of right shift operator (1 32)

It's likely the CPU is actually computing

a >> (b % 32)

in foo; meanwhile, the 1 >> 32 is a constant expression, so the compiler will fold the constant at compile-time, which somehow gives 0.

Since the standard (C++98 §5.8/1) states that

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.

there is no contradiction having foo(1,32) and 1>>32 giving different results.

 

On the other hand, in bar you provided a 64-bit unsigned value, as 64 > 32 it is guaranteed the result must be 1 / 232 = 0. Nevertheless, if you write

bar(1, 64);

you may still get 1.


Edit: The logical right shift (SHR) behaves like a >> (b % 32/64) on x86/x86-64 (Intel #253667, Page 4-404):

The destination operand can be a register or a memory location. The count operand can be an immediate value or the CL register. The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used). A special opcode encoding is provided for a count of 1.

However, on ARM (armv6&7, at least), the logical right-shift (LSR) is implemented as (ARMISA Page A2-6)

(bits(N), bit) LSR_C(bits(N) x, integer shift)
assert shift > 0;
extended_x = ZeroExtend(x, shift+N);
result = extended_x<shift+N-1:shift>;
carry_out = extended_x<shift-1>;
return (result, carry_out);

where (ARMISA Page AppxB-13)

ZeroExtend(x,i) = Replicate('0', i-Len(x)) : x

This guarantees a right shift of ≥32 will produce zero. For example, when this code is run on the iPhone, foo(1,32) will give 0.

These shows shifting a 32-bit integer by ≥32 is non-portable.

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.

Arithmetic right shift gives bogus result?

In C++ as in C, shifts are limited to the size (in bits) of the value shifted. For instance, if unsigned int is 32 bits, then a shift of more than 31 is undefined.

In practice, a common result is that the 5 least-significant bits of the shift amount are used and the higher order bits are ignored; this is due to the compiler producing a machine instruction which does exactly that (eg SHR on x86).

In this case, the shift value is 100000 (decimal) which happens to be 11000011010100000 in binary - the lower 5 bits are zero. So, you're effectively getting a shift by 0. You shouldn't rely on this however; technically, what you're seeing is undefined behaviour.

References:

For C, N1570 section 6.5.7:

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.

For C++, N3690 section 5.8 "[expr.shift]":

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.

N1570 is a draft, nearly identical to the released ISO C11 standard; this clause has been pretty much the same since the 1989 ANSI C standard.

N3690 is a recent draft of the C++ standard; I'm not sure whether it's the best one to use, but again, this clause hasn't changed.

What's bad about shifting a 32-bit variable 32 bits?

Yes, it is undefined behaviour.

ISO/IEC 9899:1999 6.5.7 Bitwise shift operators ¶3

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.

C11 aka ISO/IEC 9899:2011 says the same.

You should first cast b to the target integer type. Another point is that you should put parentheses around the macro parameters to avoid surprises by operator precedences. Additionally, the comma operator is very useful here, allowing you to avoid the braces, so that the macro can be used as a normal command, closed with a semicolon.

#define splitup(a,b,c) ( (b) = (a) >> 32, (c) = (a) & 0xffffffff )
#define combine(a,b,c) ( (a) = ((unsigned long long)(b) << 32) | (c) )

Additional casts may be necessary for `splitup to silence warnings about precision loss by over-paranoid compilers.

#define splitup(a,b,c) ( (b) = (unsigned long)((a) >> 32), (c) = (unsigned long)((a) & 0xffffffff) )

And please don't even think about using your self-written encryption for production code.

Findbugs warning: Integer shift by 32 -- what does it mean?

From the Java Language Specification:

If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x1f. The shift distance actually used is therefore always in the range 0 to 31, inclusive.

So if b is an int, the expression is identical to

long a = b | c;

which I highly doubt is what is intended. It should probably have been

long a = ((long) b << 32) | c;

(If b is already a long, the code is correct and FindBugs is mistaken about the bug).

Weird behavior of right shift in C (sometimes arithmetic, sometimes logical)

Quoting from https://en.cppreference.com/w/c/language/integer_constant, for an hexadecimal integer constant without any suffix

The type of the integer constant is the first type in which the value can fit, from the list of types which depends on which numeric base and which integer-suffix was used.


int
unsigned int
long int
unsigned long int
long long int(since C99)
unsigned long long int(since C99)

Also, later

There are no negative integer constants. Expressions such as -1 apply the unary minus operator to the value represented by the constant, which may involve implicit type conversions.

So, if an int has 32 bit in your machine, 0x80000000 has the type unsigned int as it can't fit an int and can't be negative.

The statement

int x = 0x80000000;

Converts the unsigned int to an int in an implementation defined way, but the statement

int x = 0x80000000 >> 3;

Performs a right shift to the unsigned int before converting it to an int, so the results you see are different.

EDIT

Also, as M.M noted, the format specifier %x requires an unsigned integer argument and passing an int instead causes undefined behavior.

Signed right shift = strange result?

Since x is a signed quantity, the result of (x & (0xFF << 24)) is 0xFF000000 which is also signed and thus a negative number since the top (sign) bit is set. The >> operator on int (a signed value) performs sign extension (Edit: though this behaviour is undefined and implementation-specific) and propagates the sign bit value of 1 as the value is shifted to the right.

You should rewrite the function as follows to work exclusively on unsigned values:

unsigned reverse(unsigned x)
{
unsigned int reversed = 0;

reversed = (x & (0xFF << 24)) >> 24;
reversed |= (x & (0xFF << 16)) >> 8;
reversed |= (x & (0xFF << 8)) << 8;
reversed |= (x & 0xFF) << 24;

return reversed;
}

Weird behaviour of bit-shifting with byte in Java

This happens exactly because byte is promoted to int prior performing bitwise operations. int -128 is presented as:

11111111 11111111 11111111 10000000

Thus, shifting right to 7 or 8 bits still leaves 7-th bit 1, so result is narrowed to negative byte value.

Compare:

System.out.println((byte) (b >>> 7));           // -1
System.out.println((byte) ((b & 0xFF) >>> 7)); // 1

By b & 0xFF, all highest bits are cleared prior shift, so result is produced as expected.

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



Related Topics



Leave a reply



Submit