Why doesn't this swap macro using shifts not work for negative numbers?

让人想犯罪 __ 提交于 2019-12-13 15:56:47

问题


I found some code in a built in library that I have and need to extend.

But it appears to be broken:

#define BSWAP16(__x)    ((((__x) >> 8) | ((__x) << 8)))

Does not function the same as:

__builtin_bswap16()

This program proves it.

#include <stdio.h>

#define BSWAP16(__x)    ((((__x) >> 8) | ((__x) << 8)))

int main(int argc, char* argv[])
{
    unsigned short a = (unsigned short)BSWAP16(0xff00);
    unsigned short b = __builtin_bswap16(0xff00);
    short c = (short)BSWAP16(-8);
    short d = __builtin_bswap16(-8);

    printf("a=%04x, b=%04x, c=%04x, d=%04x\n", a,b,c,d);
    return 0;
}

Output:

a=00ff, b=00ff, c=ffffffff, d=fffff8ff

I'm not wanting answers telling me I should use endian.h or __builtin_bswap16. On the target platform used by this in-house library on this platform/compiler configuration, I'm triggering this default code that is defaulting to using the above macro.

So my question is. Why doesn't it work for negative numbers?

If I represent -8 as a short value of 0xfff8 it works.

So I'm guessing it has something to do with internal conversion to int.

How do I fix this macro to work properly?


回答1:


It is undefined behavior to left shift a negative number, from the draft C99 standard section 6.5.7 Bitwise shift operators which says (emphasis mine going forward):

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 ´ 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 ´ 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

So the result of the left shift of -8 is unpredictable. Casting to unsigned short should fix the issue:

BSWAP16((unsigned short)-8)

-8 is an integer constant(literal) and since it does not have a suffix will be an int since int can take on it's value. Assuming 32-bit int and twos complement will have have the following value:

 FFFFFFF8

casting to unsigned short will remove the unwanted higher bits. Casting to unsigned int won't help since it will preserve the higher bits.

Also right shifting a negative number is implementation defined:

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or 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.




回答2:


You should mask out the unwanted bits before shifting.

#define BSWAP16(__x)    (((((__x) & 0xFF00) >> 8) | (((__x) & 0xFF) << 8)))

Regarding why it doesn't work without shifting, note that -8 is indeed 0xFFFFFFF8. Without masking, there are plenty of higher 1 bits. int is used during computation.



来源:https://stackoverflow.com/questions/24565837/why-doesnt-this-swap-macro-using-shifts-not-work-for-negative-numbers

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