Kotlin位运算
Kotlin 提供了几种用于操作整数各个位(bit) 的运算符。这些操作是由处理器直接支持的,速度快且操作简单。在底层编程中非常重要,比如设备驱动、低级图形处理、网络通信、加密和压缩等。
尽管计算机通常都有高效的硬件指令来执行算术和逻辑操作,但所有这些操作也都可以通过组合按位操作符、位移操作符和判断 0 操作来实现。
位运算符(Bitwise Operators)
共有四种位运算符:
-
inv()
:按位非(NOT),即取反或补码 -
or
:按位或(OR) -
and
:按位与(AND) -
xor
:按位异或(XOR)
这些运算符会一位一位地处理操作数的每一位,并生成一个新的数值。
-
inv()
是一元运算符,它会将每一位的 0 变为 1,1 变为 0(包括符号位也会改变)。 -
or
是二元运算符,按位“或”:只要任意一个操作数为 1,结果就是 1,否则是 0。 -
and
是按位“与”:只有两个操作数都为 1,结果才是 1,否则是 0。 -
xor
是按位“异或”:当两个操作数中正好一个为 1 时,结果是 1,否则是 0。
这些运算符不仅可以作用于整数,也可以作用于布尔类型。如果是整数,进行的是位运算;如果是布尔值,进行的是逻辑运算(除了 inv()
不能用于布尔类型)。
示例:按位与、或、异或
val first = 15 // 二进制:1111
val second = 10 // 二进制:1010val bitwiseAnd = first and second // 1111 & 1010 = 1010,结果是 10
val bitwiseOr = first or second // 1111 | 1010 = 1111,结果是 15
val bitwiseXor = first xor second // 1111 ^ 1010 = 0101,结果是 5
示例:按位取反(inv)
val first = 35 // 二进制:0..00100011
val second = -35 // 二进制:1..11011101(补码)val inverseFirst = first.inv() // ~35 = -36
val inverseSecond = second.inv() // ~-35 = 34
为什么 ~35 = -36
?这是因为 Kotlin 使用**补码(two’s complement)**来表示整数。
-
对任意正整数
n
,其按位取反是-(n + 1)
。 -
对负数
-n
,其按位取反是n - 1
。
检查一个数是否能被 2 整除(使用 and
)
val a = 5
val b = 4val bitwiseAndA = a and 1 // 101 & 001 = 001,结果是 1 => 有余数,不能整除
val bitwiseAndB = b and 1 // 100 & 001 = 000,结果是 0 => 没有余数,可以整除
位移操作符(Bit-shift Operators)
Kotlin 还提供了三种位移操作符:
-
shl
:左移(乘法),低位补零; -
shr
:带符号右移,高位填符号位(保留正负号); -
ushr
:无符号右移,高位补零(结果永远为正)。
示例:通过位移实现快速乘除法
var value = 25 // 二进制:0001 1001value = value shl 1 // 左移1位:0011 0010 => 50
value = value shl 2 // 再左移2位:1100 1000 => 200var anotherVal = 14
anotherVal = anotherVal shr 1 // 右移1位:0111 => 7
我们可以总结出:
var newVal = 25newVal = newVal shl 1 // 25 * 2^1 = 50
newVal = newVal shl 3 // 50 * 2^3 = 400
newVal = newVal shr 2 // 400 / 2^2 = 100
示例:使用位移求区间中点
val left = 10
val right = 20val mid = (left + right) shr 1 // 结果是 15
与 (left + right) / 2
相同,但位移方式更快。
示例:shr
与 ushr
的区别
val number1 = 5
val number2 = -5val shrNumber1 = number1 shr 1 // 0101 → 0010,结果是 2
val ushrNumber1 = number1 ushr 1 // 同上,结果是 2
val shrNumber2 = number2 shr 1 // 保留符号,结果是 -3
val ushrNumber2 = number2 ushr 1 // 补零,结果是 2147483645
-
shr
会根据符号保留符号位。 -
ushr
总是左边补 0,所以负数也变为正数。
Kotlin 1.6 新增:位旋转
val a = 4val shiftRight = a.rotateRight(1) // 0100 → 0010,结果是 2
val shiftLeft = a.rotateLeft(1) // 0100 → 1000,结果是 8
val b = 3val shiftLeft = b.rotateLeft(1) // 0011 → 0110,结果是 6
val shiftRight = b.rotateRight(1) // 0011 → 1..0001,结果是 -2147483647
rotateRight 会把最高位 1 移到符号位(第 31 位),结果变成负数。
运算优先级
Kotlin 中,位运算符和位移运算符的优先级低于加减乘除:
优先级(从高到低) | 运算符例子 |
---|---|
括号 | (expr) |
后缀自增自减 | expr++ , expr-- |
前缀正负、自增自减 | -expr , ++expr , --expr |
乘、除、模 | * , / , % |
加减 | + , - |
赋值及其组合 | = , += , -= , *= 等 |
位运算和位移 | and , or , xor , shl , shr , ushr |
位运算符之间是从左到右执行的。 |
例如:
val mid = left + right shr 1
无需加括号,是因为 +
的优先级高于 shr
,相当于:
val mid = (left + right) shr 1
总结
在本节中,我们学习了如何对整数的位进行操作,理解了位运算与一些算术运算之间的对应关系:
-
位运算符:逐位处理。
-
位移运算符:可以整体左移或右移一整串位。
虽然位运算看起来稍显复杂,但这是理解底层编程和高效计算的基础。