在开始时使用零右移

lau*_*ent 7 c c++ bit-manipulation bit-shift

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

0xff << 3 = 11111000
Run Code Online (Sandbox Code Playgroud)

但是,如果我改变它,我得到这个:

0xff >> 3 = 11111111
Run Code Online (Sandbox Code Playgroud)

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

00011111
Run Code Online (Sandbox Code Playgroud)

有什么建议吗?

编辑

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

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;
Run Code Online (Sandbox Code Playgroud)

输出:

fffffff0
ffffffff
Run Code Online (Sandbox Code Playgroud)

因为它似乎总体上应该有效,所以我对这个特定代码没有的原因感兴趣.任何的想法?

Lun*_*din 10

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

如果你离开了0xff << 3,你得到二元:00000000 11111111 << 3 = 00000111 11111000

如果你右转0xff >> 3,你得到二进制:00000000 11111111 >> 3 = 00000000 00011111

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

#include <stdio.h>

int main()
{

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

}
Run Code Online (Sandbox Code Playgroud)

输出:

07F8 2040
001F 31
Run Code Online (Sandbox Code Playgroud)

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


资料来源:ISO 9899:2011 6.5.7.


问题更新后编辑

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

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

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

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

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

6.5.7按位移位运算符

K&R中移位运算符的描述表明,通过长计数移位应该强制左操作数在移位之前加宽.由C89委员会认可的更直观的做法是,班次计数的类型与结果的类型无关.

C89的安静改变

通过长计数移动不再强制将移位的操作数强制为长.C89委员会肯定了K&R授予的实施自由,不要求签署权利转移操作签署延期,因为这样的要求可能会减慢快速代码的速度,并且因为符号扩展班次的有用性是微不足道的.(在一个地方算术右移一个负2的补数整数与除以2的不一样!)


Ria*_*iaD 7

如果你明确地移动0xff它就像你期望的那样工作

cout << (0xff >> 3) << endl; // 31
Run Code Online (Sandbox Code Playgroud)

只有0xff在签名宽度为8的类型(char以及signed char流行的平台上)时才应该这样.


所以,在一般情况下:

您需要使用无符号整数

(unsigned type)0xff

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

所以当你有1作为第一位时,你有负值,而在除法后它再次为.

  • 请注意,只有当它处于`signed char`类型时才需要为`0xff`的值执行此操作 - 否则`0xff`具有非负值(255).我认为应该明确指出这一点,因为我看到人们认为`0xff`在上下文中没有负值时会感到困惑. (4认同)
  • 我根本不明白这个答案.0xff相当于写入(signed int)255.原始帖子中没有任何内容设置任何符号位.0xFF >> 3为0x1F,正数为31. (2认同)

Jac*_*ley 5

你所说的两种右移称为逻辑移位算术移位.C和C++对无符号整数使用逻辑移位,并且大多数编译器将对有符号整数使用算术移位,但标准不保证这一点,这意味着右移一个负的signed int的值是实现定义的.

由于您需要逻辑移位,因此需要切换到使用无符号整数.您可以通过替换常量来实现此目的0xffU.

  • 不,当您右移负数时,C和C++会调用实现定义的行为.无法保证结果有任何意义,或者它可以表示为正确格式的负数.如果您将shift设置为负数,则调用未定义的行为. (7认同)