16

Go中的bit位和位运算符

 4 years ago
source link: https://studygolang.com/articles/28005
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一.二进制长什么样

bit比特是计算机的最小单元,是一个二进制位1或者0

t.Log("start")
t.Logf("%b", 0) //0
t.Logf("%b", 1) //1
t.Logf("%b", 2) //10
t.Logf("%b", 3) //11
/*
    int是带符号的,表示范围是:-2147483648到2147483648,即-2^31到2^31次方。
    uint则是不带符号的,表示范围是:2^32即0到4294967295。
*/
var one uint
one = 10000
t.Logf("%b", one) //10011100010000  13位,uint最多是32位

二.运算符

主要有下面几种运算符

&      位运算 AND
|      位运算 OR
^      位运算 XOR
&^     位清空 (AND NOT)
<<     左移
>>     右移

1.<<和>>左移和右移运算符

左移 i << n; 对i的所有位向左移n次

var i int64
t.Logf("%b", i) //0
i = 1
t.Logf("%b %d", i, i)       // 1 1
t.Logf("%b %d", i<<1, i<<1) //10 2  左移动 每个移位位置代表2的幂,左移动增加
t.Logf("%b %d", i<<2, i<<2) // 100 4
1<<N = 2^N
---------------
1 左移多少位等于2的多少次方
1024>>N = 1024/2^N
---------------
右移N位 相当于除以2的N次方

关于左移、右移 , 有一个Go在写磁盘单位GB MB KB等大小定义的例子

在使用const关键字的时候 可使用内置变量 iota从0开始自动递增

在遇到下个常量块或者单个常量定义的时候 也就是再一次使用const关键字的时候 iota置0

package main
type ByteSize float64
const(
  B ByteSize = 1<<(10*iota)  // 1<<(10*0)
  KB  // 1<<(10*1) 左移动10位  2的10次方=1024
  MB  // 1<<(10*2)
  GB  // 1<<(10*3)
  TB     // 1<<(10*4)
  PB //  1<<(10*5)
)

2.位运算 & AND

同一位上都是1 才1 否咋0

c, d := 1, 1
t.Logf("c&d %b %b %b", c&d, c, d) //c&d 1 1 1
c, d = 1, 0
t.Logf("c&d %b %b %b", c&d, c, d) //c&d 0 1 0
c, d = 5, 3
t.Logf("c&d %b %b %b", c&d, c, d) //c&d 1 101 011
c, d = 5, 4
t.Logf("c&d %b %b %b", c&d, c, d) //c&d 100 101 100  十进制4
c, d = 50, 43
t.Logf("c&d %b %b %b", c&d, c, d) //c&d 100010 110010 101011 十进制34

1.使用案例 判断奇偶数

odd number :指不能被2整除的数 ,数学表达形式为:2k+1, 奇数可以分为正奇数和负奇

func IsOdd(i int)bool{
  return (i & 1) == 1 //i是否为奇数取决于二进制的最后一位是1还是0 是1则为奇数 0则为偶数
}

2.使用案例 计算数值的二进制位有多少个1

很容易想到只要与1进行&运算,第一位如果为0结果为0 第一位如果结果为1 结果为1 利用这个特性数值右移一位 循环计算即可

func BitCheck(i int)(count int){
  for i>0 {
    count = count + (i&1)
    i >>= 1
  }
  return
}

2.位运算 | OR

同一位上有一个是1就1,只有都是0才0

c, d = 0, 0
t.Logf("c|d %b %b %b", c|d, c, d) //c|d 0 0 0
c, d = 1, 1
t.Logf("c|d %b %b %b", c|d, c, d) //c|d 1 1 1
c, d = 1, 0
t.Logf("c|d %b %b %b", c|d, c, d) //c|d 1 1 0
c, d = 5, 3
t.Logf("c|d %b %b %b", c|d, c, d) //c|d 111 101 011  十进制7
c, d = 5, 4
t.Logf("c|d %b %b %b", c|d, c, d) //c|d 101 101 100  十进制5
c, d = 50, 43
t.Logf("c|d %b %b %b", c|d, c, d) //c|d 111011 110010 101011  十进制59

3.异或运算符 ^ XOR

^即可作为二元运算符,也可作为一元运算符。

作为二元运算符,^是异或运算符。

即两个数的二进制位不同时,当前位才置1 否则置0

有个很明显的规律 任何数和本身异或 结果为0, 0和任意数异或 结果为其本身

a, b := 2, 3
t.Logf("%b %b", a, b) //10 11
a ^= b
t.Logf("%b %b", a, b) // 1 11
4 ^ 15 = 11
---------------
00000100 ^
00001111 =
00001011 //10进制值位11

1.使用案例 数值交换

Go中数值交换可以直接通过 a, b = b, a 这样的方式来直接交换,位运算是怎么实现交换的呢?

a ^= b // a = a^b
b ^= a // b = b^(a^b) b和b自己异或为0 相当于 b=a
a ^= b // a = (a^b)^a a在第一步中已经为a^b,现在的b 已经等于a a互相抵消 完成了值的交换

4.位清空运算符 &^ AND NOT

4 &^ 15 = 0
---------------
00000100 &^
00001111
00000000
=0

三.二元运算符号

a |= b  ----->  a = a | b  , a 或者 b 只要有一个为 1, 那么,a 的最终结果就为 1
 a &= b  ----->  a = a & b  , a 和 b 二者必须都为 1, 那么,a 的最终结果才为 1
 a ^= b  ----->  a = a ^ b  , 当且仅当 a 和 b 的值不一致时,a 的最终结果才为1,否则为0
a, b := 1, 0
a |= b
t.Logf("%b %b", a, b) // 1 0
a, b = 1, 0
a &= b
t.Logf("%b %b", a, b) // 0 0
a, b = 1, 0
a ^= b
t.Logf("%b %b", a, b) //  1 0
a, b = 2, 3
t.Logf("%b %b", a, b) //10 11
a |= b
t.Logf("%b %b", a, b) //11 11
a, b = 2, 3
a &= b
t.Logf("%b %b", a, b) // 10 11
a, b = 2, 3
a ^= b
t.Logf("%b %b", a, b) // 1 11

使用案例参考了作者niceshot

网页 https://juejin.im/post/5d9094...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK