1、关于2的补码
正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反[每一位取反(除符号位)]。
正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。
为什么负数的计算方法就是先取
反码,再取补码(+1)#即 正数取反再+1# ?
以8位计算机为例,+8就是0000 1000,-8是怎么表示呢,最简单的理解是 0 - 8, 0 我们可以用 0000 0000 表示,不够减需要向上借一位 1 0000 0000,这边0变成了9位了,为了计算方便,我们将1 0000 0000 拆成 1111 1111 + 1。
-1在计算机里用二进制表达就是全1。16进制为:0xFFFFFFFF
-1 => +1 => 0000 0000 0000 0001 => 1111 1111 1111 1110 + 1=> 0xFFFF
8位-128为什么表示为 1 0000 0000
-128 => +128 => 1000 0000 => 0111 1111 + 1 => 1000 0000
2、bit 和 byte
1 byte = 8bit,如果是带符号位则值域为-2^(n-1) 到 2^(n-1) - 1
fmt.Printf("int8'values range %d and %d\n", -1 << 7, 1 << 7 - 1)
fmt.Printf("int8'values range %d and %d\n", math.MinInt8, math.MaxInt8)
//output: int8'values range -128 and 127
3、整型
- 有符号的
int8,int16,int32,int64,int - 无符号的
unit8,uint16,uint32,uint64,uint Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点byte是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数%取模运算符的符号和被取模数的符号总是一致的,且仅用于整数间的运算,因此-5%3和-5%-3结果都是-2- 除法运算符/的行为则依赖于操作数是否为全为整数,比如5.0/4.0的结果是1.25,但是5/4的结果是1,因为整数除法会向着0方向截断余数
- 大部分场景使用有符号数,无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等
- 算术运算符
+,-,*,/整型相除还是得到整型,%取余(模) - bit位操作运算符
| or,& and,^ xor 异或,&^ 按位消除,<<,>>具体见下面的例子 - 取值范围可以用
math包来获取,如int8范围是math.MinInt8至math.MaxInt8 - 所占字节(
1byte=4bit)长度可以用unsafe.Sizeof获取
const MaxUint = ^uint(0)
const MinUint = 0
const MaxInt = int(MaxUint >> 1)
const MinInt = -MaxInt - 1
//int8'values range -128 and 127
fmt.Printf("int8'values range %d and %d\n", math.MinInt8, math.MaxInt8)
fmt.Printf("current os' int8 size is %d\n", unsafe.Sizeof(int8(1)))
fmt.Printf("current os' int16 size is %d\n", unsafe.Sizeof(int16(1)))
fmt.Printf("current os' int32 size is %d\n", unsafe.Sizeof(int32(1)))
fmt.Printf("current os' int64 size is %d\n", unsafe.Sizeof(int64(1)))
fmt.Printf("current os' int size is %d\n", unsafe.Sizeof(int(1)))
//output
//current os' int8 size is 1
//current os' int16 size is 2
//current os' int32 size is 4
//current os' int64 size is 8
//current os' int size is 8
package main
import (
"fmt"
"strconv"
)
func main() {
var x uint8 = 1<<1 | 1<<2 | 1<<3 | 1<<5
var y uint8 = 1<<1 | 1<<2 | 1<<4
fmt.Printf("x 二进制:%08b, %s\n", x, getNumberSet(int(x), 8))//00101110 set{1, 2, 3, 5}
fmt.Printf("y 二进制:%08b, %s\n", y, getNumberSet(int(y), 8))//00010110 set{1, 2, 4}
fmt.Printf("x|y 二进制:%08b\n", x|y)//或运算 00111110 set{1, 2, 3, 4, 5} 并集
fmt.Printf("x&y 二进制:%08b\n", x&y)//与运算 00000110 set{1, 2} 交集
fmt.Printf("x^y 二进制:%08b\n", x^y)//异或运算 00111000 set{3,4,5} 对称差集
fmt.Printf("x&^y二进制:%08b\n", x&^y)//按位消除 00101000 set{3, 5} 集合x相对于集合y的差集
fmt.Printf("x<<1二进制:%08b\n", x<<1)//01011100 set{2,3,4,6}
fmt.Printf("x>>1二进制:%08b\n", x>>1)//00010111 set{0,1,2,4}
}
//获取集合
func getNumberSet(num, bit int) string {
var str = "set{"
for i := 0; i < bit; i++ {
if num & (1<<i) != 0 {
str += strconv.Itoa(i) + ", "
}
}
return str + "}"
}
ascii := 'a'
unicode := '国'
newline := '\n'
fmt.Printf("%d %[1]c %[1]q %[1]T\n", ascii) // "97 a 'a'" int32
fmt.Printf("%d %[1]c %[1]q %[1]T\n", unicode) // "22269 国 '国'" int32
fmt.Printf("%d %[1]q %[1]T\n", newline) // "10 '\n'" int32
4、浮点数
- Go语言提供了两种精度的浮点数,
float32和float64 - 最大范围可以用
math包获取eg. math.MaxFloat64 math.MaxFloat32 - 所占字节(
1byte=4bit)长度可以用unsafe.Sizeof获取 - 一个
float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度;通常应该优先使用float64类型,因为float32类型的累计计算误差很容易扩散,并且float32能精确表示的正整数并不是很大(因为float32的有效bit位只有23个,其它的bit位用于指数和符号;当整数大于23bit能表达的范围时,float32的表示将出现误差) - 小数点前面或后面的数字都可能被省略(例如.707或1.)
//float32 byte size: 4, max value : 3.402823E+38
fmt.Printf("float32 byte size: %d, max value : %E\n", unsafe.Sizeof(float32(1)), math.MaxFloat32)
//float32 超过23bit就有问题
var f1, f2 float32 = 1<<23, 1<<24
fmt.Printf("f1:%[1]T f1=%[1]f,f1+1=%[2]f,f1==f1+1? %[3]t\n", f1, f1+1, f1==f1+1)
fmt.Printf("f2:%[1]T f2=%[1]f,f2+1=%[2]f,f2==f2+1? %[3]t\n", f2, f2+1, f2==f2+1)
//output
//f1:float32 f1=8388608.000000,f1+1=8388609.000000,f1==f1+1? false
//f2:float32 f2=16777216.000000,f2+1=16777216.000000,f2==f2+1? true
//小数点前面或后面的数字都可能被省略(例如.707或1.)
var f3, f4 = .19, 8.
fmt.Printf("f3: %f, %[1]T\n", f3)
fmt.Printf("f4: %f, %[1]T\n", f4)
//f3: 0.190000, float64
//f4: 8.000000, float64
5、布尔值
&&对应逻辑乘法,||对应逻辑加法,乘法比加法优先级要高,&&的优先级比||高- 不像
php,go语言中布尔值并不会隐式转换为数字值0或1,反之亦然 - 不能用
bool(int)将int转为bool类型,会报cannot convert xx (type int) to type bool反之亦然:go中定义true是0 == 0,false是0 != 0
//go 中 true 和 false 的定义
const (
true = 0 == 0 // Untyped bool.
false = 0 != 0 // Untyped bool.
)
//err:cannot convert 1 (type untyped int) to type bool
b1 := bool(1)
var i1 = 1
//err:non-bool i1 (type int) used as if condition
if i1 {
fmt.Println("Todo sth!")
}
6、字符串
- 字符串是一个
不可改变的字节序列:与php不同的地方,字符串是不能修改的 - 内置的
len函数可以返回一个字符串中的字节数目(不是rune字符数目),索引操作s[i]返回第i个字节的字节值 - 字符串转为字节数组,如果都是英文,可以用
[]byte(s1),如果有中文则应该用[]rune(s1),byte是uint8的别名,rune是int32的别名 - 字符串底层就是
Byte数组 - 只是针对于英文情况下一个英文对应一个
byte[uint8 1字节]但是中文不一定,go以utf-8形式编码,而中文在utf-8是占三个字节,如果以byte类型存中文显然会溢出,所以为了解决上述情况,go用rune[int32 4字节]类型存 []byte方法和[]rune方法是将字符串转化成对应类型的slice(slice是引用类型,引用数组),string方法是转化为字符串
//字符串不能修改
//err:cannot assign to s1[1]
var s1 = "hello loco"
s1[1] = 'x';
var s1 = "I love you,中国!"
var b1 = []byte(s1)
var r1 = []rune(s1)
//b1: []byte{0x49, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x2c, 0xe4, 0xb8, 0xad, 0xe5, 0x9b, 0xbd, 0xef, 0xbc, 0x81}, []uint8(20)
//r1: []int32{73, 32, 108, 111, 118, 101, 32, 121, 111, 117, 44, 20013, 22269, 65281}, []int32(14)
fmt.Printf("b1: %#[1]v, %[1]T(%[2]d)\n", b1, len(b1))
fmt.Printf("r1: %#[1]v, %[1]T(%[2]d)\n", r1, len(r1))
for k, v := range b1{
fmt.Printf("%03[1]d : %05[2]d\t%[2]c\t%[3]s\n", k, v, string(v))
}
for k, v := range r1{
fmt.Printf("%03[1]d : %05[2]d\t%[2]c\t%[3]s\n", k, v, string(v))
}