大部分内容选自百度百科(搜索位运算)
奇数 & 1 == 1
偶数 & 1 == 0
func main() {
fmt.Println(isOdd(3))
fmt.Println(isOdd(-3))
fmt.Println(isOdd(0))
fmt.Println(isOdd(4))
}
func isOdd(v int) bool {
return v&1 == 1
}
结果:
true
true
false
false
v := 13
v ^= v >> 1
v ^= v >> 2
v ^= v >> 4
v ^= v >> 8
v ^= v >> 16
v &= 1
以4位表示,这个只需要运行到右移2位即可:
1011 ^ (1011 >> 1) == 1110
结果的右起第1位0是原数中右起第1和第2位异或的结果(1 ^ 1),它表示后两位有奇数个1还是偶数个1.
结果的右起第2位是原数中右起第2位和第3位异或的结果,但是第2位已经统计过了,所以,这一位不考虑。
结果的右起第3位是原数中右起第3位和第4位异或的结果,1表示有奇数个1。
结果的右起第4为不考虑了。
1110 ^ (1110 >> 2) == 1101
1110中有用的位数是右起第1位和第3位,所以只考虑这两位,右移2位将第1和第3位对齐,做异或,得到的结果就是1个数的奇偶性,其余3位丢弃,所以最后使用&1的方式取最后一位。
这是分治思想的一种应用。
把最后一位变为1
1010 | 1 == 1011
把最后一位变为0
0101 | 1 - 1 == 0100
最后一位取反
0101 ^ 1 == 0100
把右数第k位变成1
(k=2) 0101 ^ (1 << k-1) == 0111
把右数第k位变成0
(k=3) 0101 & (~(1 << k-1)) == 0001
右数第k位取反
(k=2) 0101 ^ (1 << k-1) == 0111
把右边连续的1变成0
10111 & (10111 + 1) == 10000
把右起第一个0变成1
10111 | (10111 + 1) == 11111
把右边连续的0变成1
1000 ^ (1000 - 1) == 1111
取右边连续的1
(10111 ^ (10111 + 1)) >> 1 == 00111
去掉右起第一个1的左边(除了右起第一个1,其余位置0)
11100 & (11100 ^ (11100 - 1)) == 00100
或者
11100 & (-11100) == 00100
本节选自 https://graphics.stanford.edu/~seander/bithacks.html
测试代码部分使用golang
int x, y; // input values to compare signs
bool f = ((x ^ y) < 0); // true iff x and y have opposite signs
测试代码:
xarr := []int{-3, 0, 4}
yarr := []int{-3, 0, 4}
for _, x := range xarr {
for _, y := range yarr {
f := (x ^ y) < 0
if f {
fmt.Printf("%d and %d have opposite signs.\n", x, y)
} else {
fmt.Printf("%d and %d don't have opposite signs.\n", x, y)
}
}
}
结果:
-3 and -3 don't have opposite signs.
-3 and 0 have opposite signs.
-3 and 4 have opposite signs.
0 and -3 have opposite signs.
0 and 0 don't have opposite signs.
0 and 4 don't have opposite signs.
4 and -3 have opposite signs.
4 and 0 don't have opposite signs.
4 and 4 don't have opposite signs.
f = (v & (v - 1)) == 0
测试代码:
nums := []int{-5, -2, 0, 4, 5}
for _, num := range nums {
f := (num & (num - 1)) == 0
fmt.Printf("%d is a power of 2? %t \n", num, f)
}
结果:
-5 is a power of 2? false
-2 is a power of 2? false
0 is a power of 2? true
4 is a power of 2? true
5 is a power of 2? false
f = v && !(v & (v - 1))
两数相加溢出:
func CheckedAddInt64(a, b int64) (sum int64, err error) {
sum = a + b
// ^的结果>=0说明是同符号,<0说明不同符号
// 两个同符号的数相加出现不同符号的结果,说明溢出了
if ((a ^ b) >= 0) && ((a ^ sum) < 0) {
err = ErrOverflow
}
return
}
两数相减溢出:
func CheckedSubtractInt64(a, b int64) (sub int64, err error) {
sub = a - b
// a与b不同符号,相当于两个同符号的数相加,但结果与a不同,说明溢出
if ((a ^ b) < 0) && ((a ^ sub) < 0) {
err = ErrOverflow
}
return
}