1. 程式人生 > 實用技巧 >整形與浮點型

整形與浮點型

01. 整形

Go 語言中,整數型別可以再細分成10個型別,

int 和 uint 的區別就在於一個u,有u說明是無符號,沒有u代表有符號。

解釋這個符號的區別

int8uint8舉例,8 代表 8個bit,能表示的數值個數有 2^8 = 256。

uint8 是無符號,能表示的都是正數,0-255,剛好256個數。

int8 是有符號,既可以正數,也可以負數,那怎麼辦?對半分唄,-128-127,也剛好 256個數。

int8 int16 int32 int64 這幾個型別的最後都有一個數值,這表明了它們能表示的數值個數是固定的。

而 int 沒有並沒有指定它的位數,說明它的大小,是可以變化的,那根據什麼變化呢?

  • 當你在32位的系統下,int 和 uint 都佔用 4個位元組,也就是32位。

  • 若你在64位的系統下,int 和 uint 都佔用 8個位元組,也就是64位。

出於這個原因,在某些場景下,你應當避免使用 int 和 uint ,而使用更加精確的 int32 和 int64,比如在二進位制傳輸、讀寫檔案的結構描述(為了保持檔案的結構不會受到不同編譯目標平臺位元組長度的影響)

不同進位制的表示方法

出於習慣,在初始化資料型別為整形的變數時,我們會使用10進位制的表示法,因為它最直觀,比如這樣,表示整數10.

var num int = 10

不過,你要清楚,你一樣可以使用其他進位制來表示一個整數,這裡以比較常用的2進位制、8進位制和16進制舉例。

2進位制:以0b0B為字首

var num01 int = 0b1100

8進位制:以0o或者0O為字首

var num02 int = 0o14

16進位制:以0x為字首

var num03 int = 0xC

下面用一段程式碼分別使用二進位制、8進位制、16進位制來表示 10 進位制的數值:12

package main

import (
    "fmt"
)

func main() {
    var num01 int = 0b1100
    var num02 int = 0o14
    var num03 int = 0xC

    fmt.Printf("2進位制數 %b 表示的是: %d \n
", num01, num01) fmt.Printf("8進位制數 %o 表示的是: %d \n", num02, num02) fmt.Printf("16進位制數 %X 表示的是: %d \n", num03, num03) } 輸出如下 2進位制數 1100 表示的是: 12 8進位制數 14 表示的是: 12 16進位制數 C 表示的是: 12

以上程式碼用過了 fmt 包的格式化功能,你可以參考這裡去看上面的程式碼

%b    表示為二進位制
%c    該值對應的unicode碼值
%d    表示為十進位制
%o    表示為八進位制
%q    該值對應的單引號括起來的go語法字元字面值,必要時會採用安全的轉義表示
%x    表示為十六進位制,使用a-f
%X    表示為十六進位制,使用A-F
%U    表示為Unicode格式:U+1234,等價於"U+%04X"

02. 浮點型

浮點數型別的值一般由整數部分、小數點“.”和小數部分組成。

其中,整數部分和小數部分均由10進製表示法表示。不過還有另一種表示方法。那就是在其中加入指數部分。指數部分由“E”或“e”以及一個帶正負號的10進位制陣列成。比如,3.7E-2表示浮點數0.037。又比如,3.7E+1表示浮點數37

有時候,浮點數型別值的表示也可以被簡化。比如,37.0可以被簡化為37。又比如,0.037可以被簡化為.037

有一點需要注意,在Go語言裡,浮點數的相關部分只能由10進製表示法表示,而不能由8進製表示法或16進製表示法表示。比如,03.7表示的一定是浮點數3.7

float32 和 float64

Go語言中提供了兩種精度的浮點數 float32 和 float64。

float32,也即我們常說的單精度,儲存佔用4個位元組,也即4*8=32位,其中1位用來符號,8位用來指數,剩下的23位表示尾數

float64,也即我們熟悉的雙精度,儲存佔用8個位元組,也即8*8=64位,其中1位用來符號,11位用來指數,剩下的52位表示尾數

那麼精度是什麼意思?有效位有多少位?

精度主要取決於尾數部分的位數。

對於 float32(單精度)來說,表示尾數的為23位,除去全部為0的情況以外,最小為2^-23,約等於1.19*10^-7,所以float小數部分只能精確到後面6位,加上小數點前的一位,即有效數字為7位。

同理 float64(單精度)的尾數部分為 52位,最小為2^-52,約為2.22*10^-16,所以精確到小數點後15位,加上小數點前的一位,有效位數為16位。

通過以上,可以總結出以下幾點:

1. float32 和 float64 可以表示的數值很多

浮點數型別的取值範圍可以從很微小到很巨大。浮點數取值範圍的極限值可以在 math 包中找到:

  • 常量 math.MaxFloat32 表示 float32 能取到的最大數值,大約是 3.4e38;

  • 常量 math.MaxFloat64 表示 float64 能取到的最大數值,大約是 1.8e308;

  • float32 和 float64 能表示的最小值分別為 1.4e-45 和 4.9e-324。

2. 數值很大但精度有限

人家雖然能表示的數值很大,但精度位卻沒有那麼大。

  • float32的精度只能提供大約6個十進位制數(表示後科學計數法後,小數點後6位)的精度

  • float64的精度能提供大約15個十進位制數(表示後科學計數法後,小數點後15位)的精度

這裡的精度是什麼意思呢?

比如 10000018這個數,用 float32 的型別來表示的話,由於其有效位是7位,將10000018 表示成科學計數法,就是 1.0000018 * 10^7,能精確到小數點後面6位。

此時用科學計數法表示後,小數點後有7位,剛剛滿足我們的精度要求,意思是什麼呢?此時你對這個數進行+1或者-1等數學運算,都能保證計算結果是精確的

import "fmt"
var myfloat float32 = 10000018
func main()  {
    fmt.Println("myfloat: ", myfloat)
    fmt.Println("myfloat: ", myfloat+1)
}
輸出如下

myfloat:  1.0000018e+07
myfloat:  1.0000019e+07

上面舉了一個剛好滿足精度要求資料的臨界情況,為了做對比,下面也舉一個剛好不滿足精度要求的例子。只要給這個數值多加一位數就行了。

換成 100000187,同樣使用 float32型別,表示成科學計數法,由於精度有限,表示的時候小數點後面7位是準確的,但若是對其進行數學運算,由於第八位無法表示,所以運算後第七位的值,就會變得不精確。

這裡我們寫個程式碼來驗證一下,按照我們的理解下面 myfloat01 = 100000182 ,對其+5操作後,應該等於 myfloat02 = 100000187,

import "fmt"

var myfloat01 float32 = 100000182
var myfloat02 float32 = 100000187

func main() {
    fmt.Println("myfloat: ", myfloat01)
    fmt.Println("myfloat: ", myfloat01+5)
    fmt.Println(myfloat02 == myfloat01+5)
}

但是由於其型別是 float32,精度不足,導致最後比較的結果是不相等(從小數點後第七位開始不精確)

myfloat:  1.00000184e+08
myfloat:  1.0000019e+08
false

由於精度的問題,就會出現這種很怪異的現象,myfloat == myfloat +1會返回true