说起位运算,那得先说原码反码补码。
说起原码反码补码,那得先说计算机进制…
算了,我们就从头来,一次撸透这兄弟几个!
一.计算机进制
1. 十进制 :
范围 0-9
就是我们日常使用的进制方式,数字范围0-19,满10进1,就是说呢,10+1=11, 这个大家都懂就不详细举例了哈。
var num int = 3
fmt.Printf("num的十进制:%d\n", num) //打印3
2. 二进制:
这玩意就是计算机使用的进制方式, 范围只有0和1,满2进1,例如:
0000 0000 + 1 = 0000 0001
0000 0001 + 1 = 0000 0010
0000 0010 + 1 = 0000 0011
0000 0011 + 1 = 0000 0100
最后一位进1等于0
最后一位进的1加在倒数第二位
倒数第二位的1又进1等于0,把进的位加在倒数第3位
倒数第三位就等于了1
以此类推......
var num int = 3 // 十进制的数字 3
fmt.Printf("num的二进制:%b\n", num) // 打印11, 具体请看本文的进制对照图
3. 八进制:
范围0-7,满8进1, 以数字0开头表示,例如 :
var n1 int = 7 // 十进制的数字 7
fmt.Printf("7的八进制:%o\n", n1) // 打印7
var n2 int = 9 // 十进制的数字 9
fmt.Printf("9的八进制:%o\n", n2) // 打印11
n3 := 8
fmt.Printf("8的八进制:%o\n", n3) // 打印10
4. 十六进制:
范围 0-9及A-F(a-f), 满16进1,以0x或0X开头表示
0-9对应十进制的0-9,
A-F对应十进制的10-15
var n1 int = 10
fmt.Printf("10的十六进制:%X\n", n1) // 打印A
var n2 int = 12
fmt.Printf("12的十六进制:%X\n", n2) // 打印C
var n3 int = 15
fmt.Printf("15的十六进制:%X\n", n3) // 打印F
var n4 int = 16
fmt.Printf("16的十六进制:%X\n", n4) // 打印10, 满16进1
5. 进制对照图
十进制 | 二进制 | 十六进制 | 八进制 |
---|---|---|---|
0 | 0000 0000 | 0 | 0 |
1 | 0000 0001 | 1 | 1 |
2 | 0000 0010 | 2 | 2 |
3 | 0000 0011 | 3 | 3 |
4 | 0000 0100 | 4 | 4 |
5 | 0000 0101 | 5 | 5 |
6 | 0000 0110 | 6 | 6 |
7 | 0000 0111 | 7 | 7 |
8 | 0000 1000 | 8 | 10 |
9 | 0000 1001 | 9 | 11 |
10 | 0000 1010 | A | 12 |
11 | 0000 1011 | B | 13 |
12 | 0000 1100 | C | 14 |
13 | 0000 1101 | D | 15 |
14 | 0000 1110 | E | 16 |
15 | 0000 1111 | F | 17 |
16 | 0001 0000 | 10 | 20 |
17 | 0001 0001 | 11 | 21 |
以此类推 | … | … | … |
二.各种进制之间的转换
1. 各种进制 转 十进制
-
二进制转十进制
方法:从二进制的最低位(最右边的那一位)开始,最低位看作第一位,依次往左把每一位的数拿出来,乘2的位数-1次方,然后求和。
例:把二进制1110转为十进制
2的1-1次方 = 1
2的2-1次方 = 2
2的3-1次方 = 4
2的4-1次方 = 8
1110 => (0 * 2^0) + (1 * 2^1) + (1 * 2^2) + (1 * 2^3)
1110 => (0 * 1) + (1 * 2) + (1 * 4) + (1 * 8) => 0 + 2 + 4 + 8 = 14 -
八进制转十进制
方法:从八进制的最低位(最右边的那一位)开始,最低位看作第一位,依次往左把每一位的数拿出来,乘8的位数-1次方,然后求和。
例:把八进制0123转为十进制
0123 => (3 * 8^0) + (2 * 8^1) + (1 * 8^2) + (0 * 8^3)
0123 => (3 * 1) + (2 * 8) + (1 * 64) + (0 * 512) => 3 + 16 + 64 + 0 = 83 -
十六进制转十进制
方法:从十六进制的最低位(最右边的那一位)开始,最低位看作第一位,依次往左把每一位的数拿出来,乘16的位数-1次方,然后求和。
例:把十六进制0x25C(就是25C)转为十进制
25C => (12 * 16^0) + (5 * 16^1) + (2 * 16^2)
0123 => (12 * 1) + (5 * 16) + (2 * 256) => 12 + 80 + 512 = 604
2. 十进制 转 各种进制
-
十进制转二进制
方法:把十进制数一直除2,知道商为0为止,把每一次除完之后的余数反过来拼在一起,就是它对应的二进制数
例:把十进制数67转二进制
所以十进制67 => 二进制1000011 -
十进制转八进制
方法:把十进制数一直除8,知道商为0为止,把每一次除完之后的余数反过来拼在一起,就是它对应的八进制数
例:把十进制数157转八进制
所以十进制157 => 八进制235 -
十进制转十六进制
方法:把十进制数一直除16,知道商为0为止,把每一次除完之后的余数反过来拼在一起,就是它对应的十六进制数
例:把十进制数367转十六进制
所以十进制367 => 十六进制16F
3. 二进制 转 各种进制
-
二进制转八进制
方法:从最低位开始,把每3个二进制数编队为一组,不够的补0,每组进行转化为八进制
例:把二进制 11010101 转为 八进制
先转十进制:011 => 3, 010 => 2, 101 => 5
11010101 编组之后 (011)(010)(101) =>八进制 325 -
二进制转十六进制
方法:从最低位开始,把每4个二进制数编队为一组,不够的补0,每组进行转化为十六进制
例:把二进制 11010101 转为 十六进制
先转十进制:1101 => 13, 0101 => 5
11010101 编组之后 (1101)(0101) =>十六进制 D5
4. 各种进制 转 二进制
-
八进制转二进制
方法:把八进制的每一位数,转化为对应3位的二进制数, 不够的补0,或者不用补,自己看得出来也行
例:把八进制 325 转为二进制
3 => 二进制 011
2 => 二进制 010
5 => 二进制 101
所以:325 =>二进制 11010101 -
十六进制转二进制
方法:把十六进制的每一位数,转化为对应4位的二进制数, 不够的补0,或者不用补,自己看得出来也行
例:把十六进制0xD5 转为 二进制
D => 十进制 13 => 二进制 1101
5 => 二进制 0101
所以:D5 => 二进制 11010101
5. go实现十进制和二进制的转换
// 十进制转二进制
func decimalToBinary(num int) int {
var binary []int
for num != 0 {
binary = append(binary, num%2)
num = num / 2
}
res := 0
if len(binary) == 0 {
fmt.Printf("%d\n", 0)
} else {
for i := len(binary) - 1; i >= 0; i-- {
res = res*10 + binary[i]
}
}
return res
}
// 二进制转十进制
func binaryToDecimal(num int) int {
var remainder int
index := 0
decimalNum := 0
for num != 0 {
remainder = num % 10
num = num / 10
decimalNum = decimalNum + remainder*int(math.Pow(2, float64(index)))
index++
}
return decimalNum
}
三.原码,反码,补码
众所周知,1个字节=8位,即 0000 0000 - 1111 1111。
当一个字节为 1111 1111 :表示的数字是 255.
当一个字节为 0000 0000 :表示的数字是0.
所以一个字节可以存储0~255范围内的数字,总共256个值,负数的话可以存储 -128 ~ +127范围内的数值。
但是负数怎么表示呢?
所以原码,反码,补码就出来了:
类型 | 说明 | 存储范围 |
---|---|---|
原码 | 第一位为符号位,0代表正,1代表负,剩下7位表示数字本身 | -127 ~ +127 |
反码 | 正数反码与原码一致,负数反码 按照原码 按位取反,第一个符号位不变 | -127 ~ +127 |
补码 | 正数补码与原码一致,负数补码是该数反码+1 | -128 ~ +127 |
原码和反码 : 第一位用来表示符号位,所以少了一位。
补码 : 第一位既表示符号,也表示数值范围
例如:
数字 | 5 | -5 | -128 |
---|---|---|---|
原码 | 0000 0101 | 1000 0101 | 表示不了 |
反码 | 0000 0101 | 1111 1010 | 表示不了 |
补码 | 0000 0101 | 1111 1011 | 1000 0000 |
总结:
1.正数的原码,反码,补码都是一样的。
2.0的反码和补码都是0。
3.计算机都是以补码的方式进行计算的。
- 为啥计算机以补码来运算呢?
因为补码可以在不缺少位的情况下表示负数,例如 1-1在计算机内运算的时候其实是 1+(-1)
四.golang位运算
快了快了,最后亿个知识点了,肝起来朋友们!
1. 三种位运算:
符号 | 运算规则 |
---|---|
按位与 & | 二进制中比较的两位,都是1,结果为1,否则为0 |
按位或 | | 二进制中比较的两位,其中有一个是1,结果为1,否则为0 |
按位异或 ^ | 二进制中比较的两位,一位为0另一位为1,结果为1,否则为0 |
例1:
2&3 (正数的原码,反码,补码都一样)
2的原码 => 0000 0010
2的反码 => 0000 0010
2的补码 => 0000 0010
3的原码 => 0000 0011
3的反码 => 0000 0011
3的补码 => 0000 0011
因为计算机都是以补码方式来计算的
2&3 =>
2的补码 0000 0010
&
3的补码 0000 0011
----------------
算出补码 0000 0010 =>十进制 2
所以 2&3 = 2
例2:
2|3 (正数的原码,反码,补码都一样)
2|3 =>
2的补码 0000 0010
|
3的补码 0000 0011
----------------
算出补码 0000 0011 =>十进制 3
所以 2|3 = 3
例3:
2 ^ 3 (正数的原码,反码,补码都一样)
2 ^ 3 =>
2的补码 0000 0010
^
3的补码 0000 0011
----------------
算出补码 0000 0001 =>十进制 1
所以 2 ^ 3 = 1
来一个麻烦的,负数
例4:
-2^2
-2的原码 => 1000 0010
-2的反码 => 1111 1101
-2的补码 => 1111 1110
-2^2 =>
-2的补码 1111 1110
^
2的补码 0000 0010
----------------
算出补码 1111 1100 减1位=> 反码1111 1011 符号为不变按位取反=> 原码1000 0100 =>十进制 -4
所以 -2^2 = -4
2. 两种移位运算符:
同样是以补码方式进行运算
符号 | 运算规则 |
---|---|
右移 >> | 二进制中底位溢出,符号为不变,用符号位补溢出的高位 |
左移 << | 二进制中符号位不变,低位补0 |
例1:
1 >> 2 右移2位
1的补码 0000 0001 低位向右溢出两位, 最后的两位01溢出了, 再用符号位补溢出高位
所以 1 >> 2 => 0000 0001 >> 2 => 0000 0000 =>十进制 0
func TbOne() {
numA := 3
fmt.Printf("3: %08b\n", numA)
fmt.Printf("3<<1: %08b\n", numA<<1)
fmt.Printf("3<<2: %08b\n", numA<<2)
}
/*
打印 :
3: 00000011
3<<1: 00000110
3<<2: 00001100
*/
例2:
1 << 2 左移2位
1的补码 0000 0001 符号位不变, 低位补0
所以 1 << 2 => 0000 0001 << 2 => 0000 0100 =>十进制 4
func TbOne() {
numA := 120
fmt.Printf("120: %08b\n", numA)
fmt.Printf("120>>1: %08b\n", numA>>1)
fmt.Printf("120>>2: %08b\n", numA>>2)
}
/*
120: 01111000
120>>1: 00111100
120>>2: 00011110
*/
一些具体使用
func TbOne() {
var numA, numB, numC int
numA = 0
// 打印 十进制0 的二进制数
fmt.Printf("0: %08b\n", numA) // 0: 00000000
numB = numA | 1
fmt.Printf("numB(numA|1): %08b\n", numB)// numB(numA|1): 00000001
numC = numB | 1<<1
fmt.Printf("numC(numB|1<<1):%08b\n",numC)//numC(numB|1<<1):00000011
numD = 3
fmt.Printf("3:%08b\n", numD)// 3: 00000011
numE = 3 + (1 << 2) // 3的二进制 + 1<<2的二进制
fmt.Printf("3+(1<<2): %08b\n", numE) //3+(1<<2): 00000111
// 打印十进制数
fmt.Println("abc: ", numA, numB, numC) // abc: 0 1 3
fmt.Println("十进制数5的二进制=", decimalToBinary(5)) // 101
fmt.Println("二进制数11的十进制数=", binaryToDecimal(11)) // 3
// 遍历int类型变量
num := 10086
numStr := strconv.Itoa(num)
for k, v := range numStr {
fmt.Printf("k,v:%d,%d", k, v)
fmt.Println()
}
}
// 二进制数转十进制数
func binaryToDecimal(num int) int {
var remainder int
index := 0
decimalNum := 0
for num != 0 {
remainder = num % 10
num = num / 10
decimalNum = decimalNum + remainder*int(math.Pow(2, float64(index)))
index++
}
return decimalNum
}
// 十进制数转二进制数
func decimalToBinary(num int) int {
var binary []int
for num != 0 {
binary = append(binary, num%2)
num = num / 2
}
res := 0
if len(binary) == 0 {
fmt.Printf("%d\n", 0)
} else {
for i := len(binary) - 1; i >= 0; i-- {
res = res*10 + binary[i]
}
}
return res
}
打完收工,朋友们学费了吗!