按位运算

有时您可能需要在位级别处理数字。为此,系统提供了一组适用于整数类型的按位运算。

下表提供了按位运算的所有符号和说明以及结合性,并按优先级排序。

优先级

符号

说明

示例

结合性

2

~

按位补码(反转)

~e1

5

<<

左移

e1 << e2

5

>>

右移

e1 >> e2

8

&

按位“与”

e1 & e2

9

^

按位“异或”

e1 ^ e2

10

|

按位“或”

e1 | e2

整个分组中,只有按位求补运算 ~ 是一元的,其他都是二元的。

在所有情况下,如果操作数大小小于 int/uint,则会先向高阶位添加 0 位,从而扩展到 int/uint。根据操作数类型是有符号还是无符号,高阶位可能会影响符号。

Windows 标准应用程序“计算器”可以帮助我们理解数字在位级的表示。如果在“查看”菜单中选择“程序员”操作模式,程序中将出现一组切换按钮,用于选择以十六进制 (Hex)、十进制 (Dec)、八进制 (Oct) 或二进制 (Bin) 格式表示数字。后面的显示了位数。此外,还可以选择数字位宽:1、2、4 和 8 个字节。按钮可以执行所有相关运算:“非” (~)、“与” (&)、“或” (|)、“异或” (^)、“左移” (<<) 和右移 (>>)。
 
由于计算器使用有符号数字,因此在切换到十进制模式时,可能会出现负值(切记,高阶位会被解释为符号)。为方便分析,合理做法是排除出现的减号,为此,有必要选择更高一级的大小(以字节为单位)。例如,要检查 0 到 255 范围内的值(uchar,无符号单字节整数),应选择 2 个字节(否则,只有 0 到 127 的十进制值为正,而其他值将显示为负数)。

按位补码会创建一个值,其中用 0 位代替所有 1 位,而用 1 位代替所有 0 位。例如,一个全是 0 位的字节的取反得到一个全是 1 位的字节。数字 50 以按位表示为 '00110010'(字节)。按位取反得到 '11001101'。

数值一的十六进制格式为 0x0001(对应 short)。反转这些位得到 0xFFFE(参见 ExprBitwise.mq5 脚本)。

short v = ~1;  // 0xfffe = -2
ushort w = ~1// 0xfffe = 65534

按位“与”会检查两个操作数中的每个位,如果两个操作数中某一对位置均置位(设为 1),则在结果中存储 1 位。在所有其他情况下(仅在一个操作数中设置了值为 1 的位,或者在两个对应位置均设置了值为 0 的位),则在结果中写入 0 位。

若某一位在两个操作数中至少一个设置了值为 1 的位,则按位“或”运算会将 1 位写入结果中。

如果某一位在第一个或第二个操作数中设置为 1(但没有同时设置为 1),则按位“异或”在结果中的对应位置写入 1 位。以下显示了两个数字 X 和 Y 的二进制表示以及对它们进行按位运算的结果。

X       10011010   154
Y       00110111    55
 
X & Y   00010010    18
X | Y   10111111   191
X ^ Y   10101101   173

编写包含多个不同运算符的复杂表达式时,请使用圆括号进行分组以避免优先级混淆。

移位运算可进行向左 (<<) 或向右 (>>) 移位的操作,移动的位数在第二个操作数(必须为非负整数)中定义。如果因移位运算而导致位超出了内存单元的边界,左侧(对应 <<)或右侧(对应 >>)位会被丢弃。左移后,会在右侧添加对应数量的 0 位。右移后,如果操作数无符号,则在左侧添加 0 位;如果操作数有符号,则复制符号位。在后一种情况下,正数左侧添加 0 位,负数左侧添加 1 位;即保留符号。

short q = v << 5;  // 0xffc0 = -64
ushort p = w << 5// 0xffc0 = 65472
short r = q >> 5;  // 0xfffe = -2
ushort s = p >> 5// 0x07fe = 2046

在上例中,最开始的左移“销毁”了 p 变量的高阶位,而随后右移相同位数并用零填充,导致值从 0xffc0 减小至 0x07fe。

移位大小(位数)必须小于操作数类型的大小(需考虑其可能的扩展)。否则,所有初始位都将丢失。

移动 0 位则不会改变数字。

请勿将按位运算 & 和 | 与逻辑运算 && 和 || 混淆(在 上一节中介绍过)。