Welcome to Rooeye's blog

go语言位运算相关

golang rooeye 982℃ 0评论

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

转载请注明: Jinkun 的博客 » go语言位运算相关

喜欢 (5)
发表我的评论
取消评论

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址