![]() |
1
1793
移位运算符的作用与它们的名字所暗示的完全一致。他们移动比特。以下是对不同轮班操作员的简要介绍。 操作员
所有这些运算符都可以应用于整数值(
注意
另请注意
C和C++不区分右移运算符
他们只提供
(在所有主流的C和C++实现中,包括GCC和Clang/LLVM,
左移(<<)
整数作为一系列位存储在内存中。例如,将数字6存储为32位
将此位模式向左移动一个位置(
如您所见,数字向左移动了一个位置,右侧的最后一个数字被填充为零。您可能还会注意到,向左移动相当于乘以2的幂。所以
非循环换档
请注意,这些是
不
循环移动。将此值向左移动一个位置(
结果为3221225472:
“偏离末尾”的数字丢失了。它不会环绕。 逻辑右移(>>>)逻辑右移与左移相反。它们只是向右移动,而不是向左移动。例如,移动数字12:
向右移动一个位置(
所以我们看到向右移动相当于除以2的幂。 丢失的部分不见了然而,转变并不能回收“丢失”的比特。例如,如果我们改变这种模式:
向左4个位置(
然后向后移动(
一旦我们丢失了比特,就无法恢复原始价值。 算术右移(>>)算术右移与逻辑右移完全相同,除了不是用零填充,而是用最高有效位填充。这是因为最高有效位是 签名 位或区分正数和负数的位。通过填充最高有效位,算术右移是符号保持的。 例如,如果我们将此位模式解释为负数:
我们有号码2147483552。通过算术移位(-2147483552>>4)将其向右移动4个位置,我们将得到:
或拨打134217722。 所以我们看到,通过使用算术右移而不是逻辑右移,我们保留了负数的符号。我们再次看到,我们正在按2的幂进行除法运算。 |
![]() |
2
217
假设我们有一个字节:
应用一个左位移位可以让我们:
最左侧的零被移出字节,一个新的零被附加到字节的右端。 这些比特不会翻转;它们被丢弃了。这意味着如果你左移1101100,然后右移它,你将不会得到相同的结果。 向左移动N相当于乘以2 N . 向右移动N是(如果您正在使用 ones' complement )相当于除以2 N 并四舍五入到零。 只要你使用的是2的幂,比特移位就可以用于疯狂快速的乘法和除法。几乎所有低级图形例程都使用位移。 例如,在过去,我们使用13h模式(320x200 256色)进行游戏。在模式13h中,视频存储器按像素顺序布局。这意味着要计算像素的位置,您将使用以下数学公式:
现在,在那个时代,速度是至关重要的,所以我们会使用bitshift来做这个操作。 然而,320不是2的幂,所以为了解决这个问题,我们必须找出2的幂加起来等于320:
现在我们可以将其转换为左移:
最终结果如下:
现在我们得到了与以前相同的偏移量,除了我们使用了两个位移而不是昂贵的乘法运算。..在x86中,它应该是这样的(注意,我已经很久没有做过汇编了(编者按:纠正了几个错误并添加了一个32位示例):
总计:在任何古代CPU上都有28个周期。 虚拟现实系统
在同一个古老的CPU上运行12个周期。 是的,我们会努力减少16个CPU周期。 在32位或64位模式下,这两个版本都变得更短更快。像Intel Skylake这样的现代乱序执行CPU(参见 http://agner.org/optimize/ )具有非常快的硬件倍增(低延迟和高吞吐量),因此增益要小得多。AMD Bulldozer系列有点慢,特别是对于64位乘法。在Intel CPU和AMD Ryzen上,两次移位的延迟略低,但指令比乘法多(这可能会导致吞吐量降低):
vs。
编译器将为您完成此操作:看看如何
GCC, Clang, and Microsoft Visual C++ all use shift+lea when optimizing
这里要注意的最有趣的事情是
x86 has a shift-and-add instruction (
好吧,回到现代。..现在更有用的是使用位移位将两个8位值存储在16位整数中。例如,在C#中:
在C++中,如果你使用
|
![]() |
3
108
逐位操作,包括位移,是低级硬件或嵌入式编程的基础。如果你阅读设备的规范,甚至一些二进制文件格式,你会看到字节、字和双字,它们被分解为非字节对齐的位字段,其中包含各种感兴趣的值。访问这些位字段进行读/写是最常见的用法。 图形编程中的一个简单实例是,16位像素表示如下:
要获得绿色值,您可以这样做:
解释 为了获得仅绿色的值,它从偏移量5开始,到10结束(即6位长),你需要使用一个(位)掩码,当应用于整个16位像素时,它只会产生我们感兴趣的位。
合适的掩码是0x7E0,二进制为000001111100000(十进制为2016)。
要应用遮罩,请使用AND运算符(&)。
应用掩码后,您将得到一个16位数字,实际上它只是一个11位数字,因为它的MSB在第11位。绿色实际上只有6位长,所以我们需要使用右移(11-6=5)来缩小它,因此使用5作为偏移量(
同样常见的是使用比特移位进行2次幂的快速乘法和除法:
|
![]() |
4
55
比特掩码和;移位位偏移通常用于低级图形编程。例如,用32位字编码的给定像素颜色值。
为了更好地理解,用哪些部分标记的相同二进制值表示哪些颜色部分。
例如,我们想得到这个像素颜色的绿色值。我们可以通过以下方式轻松获得该值 掩饰 和 不断移动的 . 我们的面具:
逻辑的
瞧,我们有一个整数表示像素颜色中的绿色量:
这通常用于对图像格式进行编码或解码,如
|
![]() |
5
28
一个问题是,以下内容取决于实现(根据ANSI标准):
x现在可以是127(01111111)或仍然是-1(11111111)。 在实践中,通常是后者。 |
![]() |
6
25
我只是在写技巧和窍门。它可能在测试和考试中有用。
|
![]() |
7
8
请注意,在Java实现中,要移位的位数会根据源代码的大小进行调整。 例如:
等于2。你可能会认为将位向右移动65次会使所有内容归零,但实际上相当于:
这对<<>>以及>>>我还没有用其他语言试过。 |
![]() |
8
5
Bitwise运算符用于执行位级别的操作或以不同的方式操纵位。发现位操作要快得多,有时用于提高程序的效率。 基本上,位运算符可以应用于整数类型: 长的 , 国际性组织 , 短的 , 烧焦 和 字节 . 逐位移位运算符它们分为左移和右移两类。
产量:6 ,这里3的二进制表示为0…0011(考虑32位系统),因此当它移位一次时,前导零被忽略/丢失,其余31位都向左移位。最后加零。所以它变成了0…0110,这个数字的十进制表示是6。
输出:-2 在java中,负数由2的补码表示。SO,-1表示2^32-1,相当于1….11(考虑32位系统)。当移位一次时,前导位被忽略/丢失,其余31位向左移位,最后加零。因此,它变为11…10,其十进制等价值为-2。 所以,我认为你已经对左移及其工作原理有了足够的了解。
输出:8 ,由于32位系统中35的二进制表示为00…00100011,因此当我们对其进行两次右移时,前30个前导位会向右移动/移位,两个低位会丢失/忽略,前导位会添加两个零。所以,它变成了00…00001000,这个二进制表示的十进制等价物是8。 或者有一个 简单的数学技巧 找出以下代码的输出:为了推广这一点,我们可以说,x>>y=地板(x/pow(2,y))。考虑上面的例子,x=35,y=2,所以35/2^2=8.75,如果我们取下限值,那么答案是8。
输出:
但请记住,如果你取y的大值,这个技巧对y的小值是可以的,它会给你不正确的输出。
输出:-5 ,正如我上面解释的那样,编译器将负值存储为2的补码。因此,-10表示为2^32-10,在二进制表示中考虑32位系统11…0110。当我们移位/移动一次时,前31个前导位在右侧移位,低阶位丢失/被忽略。所以,它变成了11…0011,这个数字的十进制表示是-5(我怎么知道数字的符号?因为前导位是1)。 有趣的是,如果你向右移动-1,结果总是保持-1,因为符号扩展在高位中不断引入更多的1。
产量:2147483647 ,因为在32位系统中,-2表示为11…10。当我们逐位移位时,前31个前导位向右移动/移位,低位丢失/忽略,零被添加到前导位。因此,它变为011…1111(2^31-1),其十进制等效值为2147483647。 |
![]() |
9
4
Python中一些有用的位操作。 我实施了 Ravi Prakash's answer 在Python中。
|
![]() |
10
2
请注意,Windows平台上只有32位版本的PHP可用。 然后,如果你换了<<或>>超过31比特的结果是不可预料的。通常会返回原始数字而不是零,这可能是一个非常棘手的错误。 当然,如果你使用64位版本的PHP(Unix),你应该避免偏移超过63位。然而,例如,MySQL使用64位BIGINT,因此不应该有任何兼容性问题。 更新:从PHP 7 Windows开始,PHP构建最终能够使用完整的64位整数: 整数的大小取决于平台,尽管通常的最大值约为20亿(即32位有符号)。64位平台的最大值通常约为9E18,但在PHP 7之前的Windows上,该值始终为32位。 |
![]() |
11
-3
|
![]() |
minus one · 空条件和空合并运算符*与*纯布尔表示法 7 年前 |
![]() |
zaozaoer · >>java中带负字节值的and>>>运算符 7 年前 |
![]() |
Ganesh Thampi · 使用位的奇偶程序 7 年前 |
![]() |
FredMan · Rust中的<-符号是什么? 7 年前 |
![]() |
user9578589 · Java条件数组 7 年前 |
![]() |
Francisco José Letterio · 无法理解指针操作 7 年前 |
![]() |
Danny · 如何在r中键入grep中的运算符 7 年前 |
![]() |
David Rector · 什么是?=操作员在Swift中做什么? 7 年前 |
|
Gian · “/”操作符在Lua中提供什么功能? 7 年前 |