我正在尝试做一种左移,在开头添加零而不是一些。例如,如果我离开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
因为看起来它通常应该有效,所以我对这个特定代码没有的原因感兴趣。有什么想法吗?
答案 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(char
和signed char
)类型时,才应该这样做。
所以,通常情况下:
您需要使用无符号整数
(unsigned type)0xff
右移作为2除法(如果我理解正确,则向下舍入)。
因此,当你的第一位为1时,你有负值,而在除法之后它再次为否定。
答案 2 :(得分:5)
您所谈论的两种右移称为Logical Shift和Arithmetic 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 为零,我知道这不是合适的算术轮班但会完成工作