在开始时使用零右移

时间:2013-01-18 10:25:53

标签: c++ c bit-manipulation bit-shift

我正在尝试做一种左移,在开头添加零而不是一些。例如,如果我离开0xff,我会得到这个:

0xff << 3 = 11111000

然而,如果我正确转移它,我明白了:

0xff >> 3 = 11111111

我可以使用任何操作来获得相当于左移的操作吗?即我想得到这个:

00011111

有什么建议吗?

修改

要回答评论,这是我正在使用的代码:

int number = ~0;
number = number << 4;   
std::cout << std::hex << number << std::endl;

number = ~0;
number = number >> 4;
std::cout << std::hex << number << std::endl;

输出:

fffffff0
ffffffff

因为看起来它通常应该有效,所以我对这个特定代码没有的原因感兴趣。有什么想法吗?

5 个答案:

答案 0 :(得分:10)

这就是C和二进制算法的工作原理:

如果您离开0xff << 3,则会得到二进制文件:00000000 11111111 << 3 = 00000111 11111000

如果你右移0xff >> 3,你会得到二元:00000000 11111111 >> 3 = 00000000 00011111

0xff是一个带有正值255的(带符号)int。由于它是积极的,因此转移它的结果是C和C ++中明确定义的行为。它不会做任何算术转换,也不会做任何类型或定义不明确的行为。

#include <stdio.h>

int main()
{

  printf("%.4X %d\n", 0xff << 3, 0xff << 3);
  printf("%.4X %d\n", 0xff >> 3, 0xff >> 3);

}

输出:

07F8 2040
001F 31

所以你在程序中做了一些奇怪的事情,因为它没有按预期工作。也许你正在使用char变量或C ++字符文字。


资料来源:ISO 9899:2011 6.5.7。


问题更新后编辑

int number = ~0;给出一个等于-1的负数,假设有两个补码。

number = number << 4;调用未定义的行为,因为你左移了一个负数。该程序正确地实现了未定义的行为,因为它要么做什么,要么什么都不做。它可以打印fffffff0或者它可以打印粉红色的大象,或者它可以格式化硬盘。

number = number >> 4;调用实现定义的行为。在您的情况下,您的编译器会保留符号位。这被称为arithmetic shift,并且算术右移以这样的方式工作,即MSB充满其在移位之前具有的任何比特值。因此,如果您有一个负数,您将体验到该程序正在“转移”。

在99%的实际案例中,对有符号数使用按位运算符是没有意义的。因此,始终确保使用无符号数字,并且C / C ++中没有任何危险的隐式转换规则将它们转换为带符号的数字(有关危险转换的更多信息,请参阅“整数提升规则”和“通常的算术转换” “,关于那些关于SO的很多好消息。”

编辑2 ,来自C99标准的基本原理文件V5.10中的一些信息:

  

6.5.7按位移位运算符

     

K&amp; R中的移位运算符的描述表明转换为a   long count应该强制将左操作数加宽到很久以前   被转移。更直观的做法,得到了C89的认可   委员会,是班次计数的类型没有影响   结果的类型。

     

C89的安静改变

     

长计数移动不再强制移位的操作数   长。 C89委员会肯定了授予的执行自由   由K&amp; R不需要签名的右移操作来签名   扩展,因为这样的要求可能会减慢快速代码的速度   符号扩展班次的有用性是微不足道的。 (转移一个   负两个补码整数算术右一个地方   除以两个不一样!)

答案 1 :(得分:7)

如果你明确地移动0xff它可以正常工作

cout << (0xff >> 3) << endl; // 31

只有当0xff处于带宽平台的有符号宽度8(charsigned char)类型时,才应该这样做。


所以,通常情况下:

您需要使用无符号整数

(unsigned type)0xff

右移作为2除法(如果我理解正确,则向下舍入)。

因此,当你的第一位为1时,你有值,而在除法之后它再次为否定

答案 2 :(得分:5)

您所谈论的两种右移称为Logical ShiftArithmetic Shift。 C和C ++对无符号整数使用逻辑移位,并且大多数编译器将对有符号整数使用算术移位,但标准不保证这一点,即正确移位负符号int的值是实现定义的。

由于您需要逻辑移位,因此需要切换到使用无符号整数。您可以将常量替换为0xffU来执行此操作。

答案 3 :(得分:1)

在这里加上我的5美分...... 我面临与this.lau完全相同的问题!我对此做了一些敷衍的研究,这些是我的结果:

typedef unsigned int Uint;
#define U31 0x7FFFFFFF
#define U32 0xFFFFFFFF

printf ("U31 right shifted: 0x%08x\n", (U31 >> 30));
printf ("U32 right shifted: 0x%08x\n", (U32 >> 30));

Output:
U31 right shifted: 0x00000001 (expected)
U32 right shifted: 0xffffffff (not expected)

对于Mac OS X v5.0.1,XCode中的C编译器会出现(在没有任何详细知识的情况下)将MSB保留为随每个班次拉动的进位。

有些恼人,反之亦然: -

#define ST00 0x00000001
#define ST01 0x00000002

printf ("ST00 left shifted: 0x%08x\n", (ST00 << 30));
printf ("ST01 left shifted: 0x%08x\n", (ST01 << 30));

Output:
ST00 left shifted: 0x40000000
ST01 left shifted: 0x80000000

我完全赞同上面的人断言操作数的符号与移位运算符的行为无关。

任何人都可以对Posix4实现C的规范有所了解吗?我觉得一个明确的答案可能就在那里。

与此同时,似乎唯一的解决方法是沿着以下几行的构造; -

#define CARD2UNIVERSE(c) (((c) == 32) ? 0xFFFFFFFF : (U31 >> (31 - (c))))

这很有效 - 令人生气但又必要。

答案 4 :(得分:0)

以防万一如果您希望右移后负数的第一位为 0,我们可以做的是将该负数与 INT_MIN 进行异或,这将使其 msb 为零,我知道这不是合适的算术轮班但会完成工作