go 语言里面没有逻辑左移和逻辑右移,比如想获取一个 int32 类型的数字a的符号位:
sign = a >>> 31
这种写法在java一些语言里面是没有问题的,如果a是正数,sign结果为0,反之负数为1。但是golang里只有算术左移和算术右移:
sign = a >> 31
如果 a 是正数,sign 结果为0,反之负数算术右移,高位补符号位,得到的sign就是-1。
用算术移位实现逻辑移位也简单,当a为负数的时候,把右移时高位补充的1变为0就可以了:
sign = a >> 31 & 1
这样 a 为 负数的时候得到的sign结果为1,正数的时候为0 。
如果用算术右移可以判断一个数字的符号位,那么就可以不用比较符号实现两个数字的大小比较了:
func getMaxNum(a int,b int) int{ c := a - b x := (c >> 31) & 1 ^ 1 y := x ^ 1 return a*x +b*y }
这个方法用于比较a和b两个数字的大小 :
c := a - b x := (c >> 31) & 1 ^ 1
当 c 为 负数的时候,x 为0,反之为1。
这个方法有个问题,当 a和b同号的时候,c不会溢出,当 a 和 b异号的时候,则可能会溢出,比如 a为20亿,b为负20亿,解决方法如下:
func getMaxNum(a int,b int) int{ c := a - b x := (c >> 31) & 1 ^ 1 y := x ^ 1 t := (a >> 31) & 1 ^ 1 p := (b >> 31) & 1 ^ 1 r1 := a*x + b*y r2 := a*t + b*p s1 := t ^ p ^ 1 s2 := s1 ^ 1 return r1 * s1 + r2 * s2 }
a和b同号时返回r1, a和b异号时返回r2 , 异号的时候返回符号位为0的那个数字就可以了。
还可以通过位运算比较优雅的实现 KiB,KB,MiB,MB 这些值的初始化,KiB 和 KB 的区别是 :
1KiB = 1024 B 1KB = 1000 B
平常买的4G或8G的U盘,插电脑上看都比4G或8G小,原因就是计算方法不一样,4G 的U盘实际大小是:
4GB = 4 * (10^9) B = 3.725 GiB
所以实际显示就小很多了,还有比如操作系统4G内存裸机实际查看的时候也就3点多G,golang 位运算定义 KiB,GiB十分优雅:
const ( _ = 1 << (10 * iota) KiB // 1024 MiB // 1048576 GiB // 1073741824 TiB // 1099511627776 PiB // 1125899906842624 EiB // 1152921504606846976 ZiB // 1180591620717411303424 YiB // 1208925819614629174706176 )
iota 是常量生成器,从 0 开始每次加1。
另外golang中是没有”~”运算符的,在c语言中,”~”是一元运算符,对bit位用来取反操作:
~0 = 1 ~1 = 0
那么在golang中 取反是怎么做的呢? 是这个: “^”,没错,就是用于异或计算的运算符
在golang中,”^” 用于一元运算时等价于c语言中的”~”, “^” 用于二元运算时等价于c语言中的”^”.
a &^b 等价于 a & (^b) 等价于c语言中的 a&(~b) ^a 等价于 c语言中的 ~a a^b 等价于 c语言中的 a^b
&^ 基本运算法则如下:
1 &^ 1 = 0 1 &^ 0 = 1 0 &^ 1 = 0 0 &^ 0 = 0