4.5 基本类型和运算符

时间:2022-10-10 17:01:31

整型 int 和浮点型 float

Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码(详情参见 二的补码 页面)。

Go 也有基于架构的类型,例如:int、uint 和 uintptr。

这些类型的长度都是根据运行程序所在的操作系统类型所决定的:

  • int 和 uint 在 32 位操作系统上,它们均使用 32 位(4 个字节),在 64 位操作系统上,它们均使用 64 位(8 个字节)。
  • uintptr 的长度被设定为足够存放一个指针即可。

Go 语言中没有 float 类型。

与操作系统架构无关的类型都有固定的大小,并在类型的名称中就可以看出来:

整数:

  • int8(-128 -> 127)
  • int16(-32768 -> 32767)
  • int32(-2,147,483,648 -> 2,147,483,647)
  • int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)

无符号整数:

  • uint8(0 -> 255)
  • uint16(0 -> 65,535)
  • uint32(0 -> 4,294,967,295)
  • uint64(0 -> 18,446,744,073,709,551,615)

浮点型(IEEE-754 标准):

  • float32(+- 1e-45 -> +- 3.4 * 1e38)
  • float64(+- 5 * 1e-324 -> 107 * 1e308)

int 型是计算最快的一种类型。

整型的零值为 0,浮点型的零值为 0.0。

float32 精确到小数点后 7 位,float64 精确到小数点后 15 位。由于精确度的缘故,你在使用 == 或者 != 来比较浮点数时应当非常小心。你最好在正式使用前测试对于精确度要求较高的运算。

你应该尽可能地使用 float64,因为 math 包中所有有关数学运算的函数都会要求接收这个类型。

你可以通过增加前缀 0 来表示 8 进制数(如:077),增加前缀 0x 来表示 16 进制数(如:0xFF),以及使用 e 来表示 10 的连乘(如: 1e3 = 1000,或者 6.022e23 = 6.022 x 1e23)。

你可以使用 a := uint64(0) 来同时完成类型转换和赋值操作,这样 a 的类型就是 uint64。

Go 中不允许不同类型之间的混合使用,但是对于常量的类型限制非常少,因此允许常量之间的混合使用

package main

import "fmt"

func main() {
    /*
        var a int
        var c int32
        a = 15
        b = a + a // 编译错误cannot use a + a (type int) as type int32 in assignment
        c = b + b
        c = c + 5 // 因为 5 是常量,所以可以通过编译
    */
    var n int16 = 34
    var m int32
    var b float64 = 789.11111
    // compiler error: cannot use n (type int16) as type int32 in assignment
    //m = n
    m = int32(n)

    fmt.Printf("32(m) bit int is: %d\n", m)

    fmt.Printf("16(n) bit int is: %d\n", n)

    //%0.3f(%n.m[f,g,e]) 保留小数点3位,用于表示数字 n 并精确到小数点后 m 位
    fmt.Printf("64(b) bit float is: %0.3f\n", b)
}

 

 

这里在写一个类型转换案例:

int转成uint8和float64 转int32

package main

import "fmt"
import "math"

func main() {
    var n int = 100
//math 都是基于float64来写的所以这里计算就都用64的 var j float64 = 100.6 //结果返回2个参数这里就必须用2个变量来接收结果 result, err := Uint8FromInt(n) fmt.Println(result, err) resul_int := IntFromFloat64(j) fmt.Println("IntFromFloat64:", resul_int) } /*将int安全的转成uint8,函数名Uint8FromInt指定参数和参数类型(n int) 指定返回的类型(uint8, error)*/ func Uint8FromInt(n int) (uint8, error) { //math.MaxUint8 这个就是Uint8最大的值255,16是65535 。满足2个条件的时候为真 if 0 <= n && n <= math.MaxUint8 { // conversion is safe //返回数据的时候必须符合定义(uint8, error)的类型 return uint8(n), nil } //0这里没有指定类型,uint8包括0所以这里没有报错。 return 0, fmt.Errorf("%d is out of the uint8 range", n) } func IntFromFloat64(x float64) int { //int32是有符号的所以这个范围就是max和min if math.MinInt32 <= x && x <= math.MaxInt32 { // x lies in the integer range //math.Modf(x)返回2个结果 :whole整数部分,fraction小数部分。 whole, fraction := math.Modf(x) //小数部分大于0.5为真 if fraction >= 0.5 { //下面的这个等于这个whole = whole + 1 whole++ } return int(whole) } panic(fmt.Sprintf("%g is out of the int32 range", x)) }

结果:

4.5 基本类型和运算符

 

一元运算符

  • 按位补足 ^

    该运算符与异或运算符一同使用,即 m^x,对于无符号 x 使用“全部位设置为 1”,对于有符号 x 时使用 m=-1。例如:

    ^2 = ^10 = -01 ^ 10 = -11
  • 位左移 <<

    • 用法:bitP << n
    • bitP 的位向左移动 n 位,右侧空白部分使用 0 填充;如果 n 等于 2,则结果是 2 的相应倍数,即 2 的 n 次方。例如:

      1 << 10 // 等于 1 KB
      1 << 20 // 等于 1 MB
      1 << 30 // 等于 1 GB
  • 位右移 >>

    • 用法:bitP >> n
    • bitP 的位向右移动 n 位,左侧空白部分使用 0 填充;如果 n 等于 2,则结果是当前值除以 2 的 n 次方。

当希望把结果赋值给第一个操作数时,可以简写为 a <<= 2 或者 b ^= a & 0xffffffff

位左移常见实现存储单位的用例

使用位左移与 iota 计数配合可优雅地实现存储单位的常量枚举:

 

  

package main

import "fmt"

type ByteSize float64

//1.048576e+06
const (
    _           = iota // 通过赋值给空白标识符来忽略值
    KB ByteSize = 1 << (10 * iota)
    MB
    GB
    TB
    PB
    EB
    ZB
    YB
)

func main() {
    //这里是2的10次方,第一次iota为空KB=1024字节,MB=1024*1024(个人理解iota=1024)
    fmt.Println("KB:", KB)
    fmt.Println("MB:", MB)
    fmt.Println("GB:", GB)
}

结果:可以把数换小一点这样可以很简单的看出效果

4.5 基本类型和运算符

 

在来一个应用实例:

package main

import "fmt"

type ByteSize float64

type BitFlag int

const (
    Active  BitFlag = 11 << iota // 1 << 0 == 1
    Send                         // 1 << 1 == 2
    Receive                      // 1 << 2 == 4
    test
)

func main() {
    //Active + Send + Receive=7
    flag := Active | Send | Receive
    /*
            这里用的是2的倍数,第一次iota 没有值,1 、2 、4、8、16、32、64"1 <<"开始的数 ,如果起始数是3:3、6、12、24、48
    */fmt.Println("flag:", Active)
    fmt.Println("flag:", Send)
    fmt.Println("flag:", Receive)
    fmt.Println("flag:", test)
    fmt.Println("flag:", flag)
}

结果:

4.5 基本类型和运算符

 

 

算术运算符

常见可用于整数和浮点数的二元运算符有 +-* 和 /

(相对于一般规则而言,Go 在进行字符串拼接时允许使用对运算符 + 的重载,但 Go 本身不允许开发者进行自定义的运算符重载)

/ 对于整数运算而言,结果依旧为整数,例如:9 / 4 -> 2

取余运算符只能作用于整数:9 % 4 -> 1

整数除以 0 可能导致程序崩溃,将会导致运行时的恐慌状态(如果除以 0 的行为在编译时就能被捕捉到,则会引发编译错误)

浮点数除以 0.0 会返回一个无穷尽的结果,使用 +Inf 表示:

你可以将语句 b = b + a 简写为 b+=a,同样的写法也可用于 -=*=/=%=

对于整数和浮点数,你可以使用一元运算符 ++(递增)和 --(递减),但只能用于后缀:

4.5 基本类型和运算符

     同时,带有 ++ 和 -- 的只能作为语句,而非表达式,因此 n = i++ 这种写法是无效的,其它像 f(i++) 或者 a[i]=b[i++]这些可以用于 C、C++ 和 Java 中的写法在 Go 中也是不允许的。

在运算时 溢出 不会产生错误,Go 会简单地将超出位数抛弃。如果你需要范围无限大的整数或者有理数(意味着只被限制于计算机内存),你可以使用标准库中的 big 包,该包提供了类似 big.Int 和 big.Rat 这样的类型

package main

import "fmt"

func main() {

    a := 1
    a++    //这样 OK,但是不能b := a++
    a += a //这样写是OK的
    fmt.Println("a:", a)
}

 

 

随机数

一些像游戏或者统计学类的应用需要用到随机数。rand 包实现了伪随机数的生成。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    //i++真的等于i += i?有兴趣的可以试试,随机三组数
    for i := 0; i < 3; i++ {
        a := rand.Int()
        if i == 2 {
            fmt.Printf("%d  \n", a)
        } else {
            fmt.Printf("%d / ", a)
        }
    }
    //从0-8中随机
    for i := 0; i < 3; i++ {
        r := rand.Intn(8)
        if i == 2 {
            fmt.Printf("%d  \n", r)
        } else {
            fmt.Printf("%d / ", r)
        }
    }
    //随机浮点数
    timens := int64(time.Now().Nanosecond())
    rand.Seed(timens)
    for i := 0; i < 3; i++ {
        fmt.Printf("%0.3f / ", 100*rand.Float32())
    }
}

输出:

4.5 基本类型和运算符

运算符与优先级

4.5 基本类型和运算符

当然,你可以通过使用括号来临时提升某个表达式的整体运算优先级

 

 

引入包来计算 :

目录结构(这个跟python差不多,但是很多新手会出错):

4.5 基本类型和运算符

 

 init包代码,init函数是在main函数之前执行的,注意看 "package"名字。写的是目录的名字

package test

import "math"

var Pi float64

func init() {
    Pi = 2 * math.Atan(1) // init() function computes Pi
}

 

test.go文件内容

package main

import (
    "fmt"

    "./test"
)

func main() {
    fmt.Println(test.Pi + 100)
}

执行结果:

4.5 基本类型和运算符