C shift right not working correctly on int types in my program

本小妞迷上赌 提交于 2019-12-24 08:19:19

问题


I have the following function in C:

int lrot32(int a, int n)
{
    printf("%X SHR %d = %X\n",a, 32-n, (a >> (32-n)));
    return ((a << n) | (a >> (32-n)));
}

When I pass as arguments lrot32(0x8F5AEB9C, 0xB) I get the following:

8F5AEB9C shr 21 = FFFFFC7A

However, the result should be 47A. What am I doing wrong?

Thank you for your time


回答1:


int is a signed integer type. C11 6.5.7p4-5 says the following:

4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. [...] If E1 has a signed type and nonnegative value, and E1 x 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

5 The result of E1 >> E2 is E1 right-shifted E2 bit positions. [...] if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined.

Thus in the case of <<, if the shifted value is negative, or the positive value after shift is not representable in the result type (here: int), the behaviour is undefined; in the case of >>, if the value is negative the result is implementation defined.

Thus in either case, you'd get results that at least depend on the implementation, and in the case of left-shift, worse, possibly on the optimization level and such. A strictly conforming program cannot rely on any particular behaviour.


If however you want to target a particular compiler, then check its manuals on what the behaviour - if any is specified - would be. For example GCC says:

The results of some bitwise operations on signed integers (C90 6.3, C99 and C11 6.5).

Bitwise operators act on the representation of the value including both the sign and value bits, where the sign bit is considered immediately above the highest-value value bit. Signed ‘>>’ acts on negative numbers by sign extension. [*]

As an extension to the C language, GCC does not use the latitude given in C99 and C11 only to treat certain aspects of signed ‘<<’ as undefined. However, -fsanitize=shift (and -fsanitize=undefined) will diagnose such cases. They are also diagnosed where constant expressions are required.

[*] sign extension here means that the sign bit - which is 1 for negative integers, is repeated by the shift amountwhen right-shift is executed - this is why you see those Fs in the result.

Furthermore GCC always requires 2's complement representation, so if you would always use GCC, no matter which architecture you're targeting, this is the behaviour you'd see. Also, in the future someone might use another compiler for your code, thus causing other behaviour there.


Perhaps you'd want to use unsigned integers - unsigned int or rather, if a certain width is expected, then for example uint32_t, as the shifts are always well-defined for it, and would seem to match your expectations.


Another thing to note is that not all shift amounts are allowed. C11 6.5.7 p3:

[...]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.

Thus if you ever shift an unsigned integer having width of 32 bits by 32 - left or right, the behaviour is undefined. This should be kept in mind. Even if the compiler wouldn't do anything wacky, some processor architectures do act as if shift by 32 would then shift all bits away - others behave as if the shift amount was 0.



来源:https://stackoverflow.com/questions/44859876/c-shift-right-not-working-correctly-on-int-types-in-my-program

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!