上一篇我們裝好環境,也跑出了第一支 Hello World。接下來要寫真正的程式,第一步是變數與型別。在 Python 中,變數想放什麼都行,型別要等程式跑起來才知道;而 Go 與 Python 不同,它是靜態型別的語言,也就是每個變數的型別必須在編譯時就定下來,若型別對不上,編譯器會直接拒絕並報錯。

這篇會先從 Go 的型別以及宣告變數的兩種寫法,接著討論型別轉換以及型別相關的議題。

Go 的變數

Go 的每個變數都有固定型別,宣告時可以明寫,也可以讓編譯器從值推出來。在 Go 中,我們有兩種宣告變數的方式,分別是用 var,以及使用 := 的簡短宣告。

使用 var 宣告

var 是最完整的寫法,格式為

1
var 名稱 型別 = 值

例如:

1
2
var name string = "gopher"
var age int = 3

不過型別也可以省略,Go 會直接從右邊的值推出來。例如下面的 name 會是 string,age 會是 int:

1
2
var name = "gopher"
var age = 3

使用 := 宣告

在函式裡面,更常見的寫法是 :=,它一次完成宣告與賦值,型別同樣由值推出來:

1
2
3
4
5
func main() {
name := "gopher"
age := 3
fmt.Println(name, age)
}

:= 和 var 最大的差別是位置:var 在函式內外都能用,:= 只能寫在函式內。如果寫在函式外,編譯時會出現這個錯誤:

1
syntax error: non-declaration statement outside function body

函式外只能放宣告,也就是以 var、const、func、type 開頭的敘述,而 := 不是宣告關鍵字。兩種寫法的使用位置整理如下:

位置 var :=
函式外 可以 不行
函式內 可以 可以(較常用)

賦值與重複宣告

若預期宣告之後,變數會想要更換值,則要使用 =。:= 的意思是宣告一個新變數,所以對已存在的變數再用一次 := 會出現編譯錯誤:

1
2
3
4
5
age := 3
age = 4 // 可以:換值
age := 5 // 編譯錯誤

// no new variables on left side of :=

另外,型別一旦決定就不能換。例如下面 age 已經定義型別為 int,因此將字串賦給它也會出現編譯錯誤:

1
2
3
4
age := 3
age = "four" // 編譯錯誤

// cannot use "four" (untyped string constant) as int value in assignment

而一次宣告多個變數時,:= 的左邊只要至少有一個新名字就可以通過編譯,已存在的名字只會被賦值,新名字才是真的宣告。

1
2
3
4
5
age, name := 3, "gopher"
age, city := 4, "Taipei" // age 被賦值,city 是新變數
fmt.Println(age, name, city)

// 4 gopher Taipei

Go 的型別

型別決定一個變數能存哪些值,以及這些值能做哪些運算 (The Go Project, 2026, Types)。Go 的型別分成兩類:Go 預先宣告好的型別,以及用現有型別組合出來的複合型別。下表整理了 Go 的所有型別,點開整數和複合型別則可以看每個型別各自的說明:

分類型別說明
布林bool真值 true 與 false
展開查看更多共 13 種
浮點數float32IEEE 754 的 32 位元浮點數
float64IEEE 754 的 64 位元浮點數
複數complex64實部與虛部皆為 float32 的複數
complex128實部與虛部皆為 float64 的複數
字串string位元組的序列,內容建立後不能修改
展開查看更多共 8 種

本篇接下來先介紹整數、浮點數、字串與布林值,其他型別留待後續提到相關內容再詳細說明與討論。

整數

Go 的整數型別依兩件事分類:占幾個位元,以及能不能表示負數:

大小 有號整數(可為負) 無號整數(不可為負)
指定位元數 int8、int16、int32、int64 uint8、uint16、uint32、uint64
由機器決定 int uint
  • 名稱後面的數字就是位元數,例如 int8 就占 8 位元
  • int 和 uint 沒有指定位元數,大小由機器決定。在 64 位元的機器上是 64 位元

不同位元數能表示的數字不一樣。在 Python 裡整數沒有上限,不需要考慮這件事,但在 Go 中則需要特別留意。

整數的範圍

$n$ 位元的無號整數範圍是 $0$ 到 $2^n - 1$,有號整數範圍是 $-2^{n-1}$ 到 $2^{n-1} - 1$。

展開查看數學推導

位元數與範圍的關係可以推導出來:假設設定 $n$ 個位元,每個位元有 0 和 1 兩種可能,因此一共有 $2^n$ 種不同的組合,也就是最多能表示 $2^n$ 個不同的整數。

無號整數把這些組合全部拿來表示非負整數,從 0 開始連續往上數,所以範圍是

有號整數則要同時表示負數與非負數,因此把 $2^n$ 個組合對半分,各得 $2^{n-1}$ 個。負數這一半是 $-1, -2, \cdots, -2^{n-1}$,非負數這一半是 $0, 1, \cdots, 2^{n-1} - 1$。其中 0 占去了非負這一半的一個位置,所以正數會比負數少一個,範圍是

範例

以 int8 為例,$n = 8$,代入式 $\eqref{eq:int-range}$ 可得

而 uint8 代入式 $\eqref{eq:uint-range}$ 則是 $0 \leq x \leq 2^{8} - 1 = 255$。

依照相同方式,可以得到各型別的範圍 (The Go Project, 2026, Numeric types):

有號整數 位元數 最小值 最大值
int8 8 -128 127
int16 16 -32,768 32,767
int32 32 -2,147,483,648 2,147,483,647
int64 64 -9,223,372,036,854,775,808 9,223,372,036,854,775,807
無號整數 位元數 最小值 最大值
uint8 8 0 255
uint16 16 0 65,535
uint32 32 0 4,294,967,295
uint64 64 0 18,446,744,073,709,551,615

整數溢位

如果運算的結果超出範圍,會發生什麼事?先看一個例子:

1
2
3
4
5
var x int8 = 127
x = x + 1
fmt.Println(x)

// -128

這段程式可以編譯,執行時也不會報錯,127 + 1 卻變成了 -128。這是因為超出範圍之後,Go 不會報錯或中止程式 (The Go Project, 2026, Integer overflow),而是繞回範圍的另一端1,就像里程表從 999999 跳回 000000。

繞回的規則是:$n$ 位元整數只能保留 $2^n$ 種結果,所以任意整數 $x$ 實際得到的值,是與 $x$ 除以 $2^n$ 餘數相同、且落在範圍內的那一個數。

展開查看數學推導

把 $x$ 先平移 $2^{n-1}$,使範圍變成從 0 開始,取餘數後再平移回去,即可得到

範例

int8 的 $n = 8$,所以 $2^{n-1} = 128$,$2^n = 256$。

$x = 127 + 1 = 128$:

$x = 100 + 100 = 200$:

$x = -128 - 1 = -129$:

實際撰寫程式進行驗證:

1
2
3
4
5
6
7
8
9
10
11
var x1 int8 = 127
var x2 int8 = 100
var x3 int8 = -128

fmt.Println(x1 + 1)
fmt.Println(x2 + 100)
fmt.Println(x3 - 1)

// -128
// -56
// 127

無號整數的範圍本來就從 0 開始,不需要平移,繞回的規則簡化為

例如 uint8 的 255 + 1,代入式 $\eqref{eq:wrap-unsigned}$ 得 $256 \bmod 256 = 0$,實際執行也是 0。

要注意的是,編譯器只能檢查它在編譯時就知道值的情況。把常數寫死在程式裡,超出範圍會直接編譯錯誤:

1
2
3
var x int8 = 128

// cannot use 128 (untyped int constant) as int8 value in variable declaration (overflows)

但如果是變數運算的結果,編譯器在編譯時並不知道值,所以只能等到執行時繞回,不會有任何提示。這是整數溢位容易變成 bug 的原因,使用時要先估算數值會不會超出型別的範圍。

浮點數

帶小數的數字使用浮點數,Go 有 float32 和 float64 兩種,數字代表位元數,通常使用 float64。Go 沒有叫做 float 的型別,寫了會出現編譯錯誤:

1
2
3
var a float = 3

// undefined: float

兩者都是 IEEE 754 的浮點數2 (The Go Project, 2026, Numeric types),差別主要在精度,其次是範圍:

型別 位元數 有效位數 $p$ 約等於十進位 最大值 最接近 0 的正數
float32 32 24 位元 約 7 位 $3.4 \times 10^{38}$ $1.4 \times 10^{-45}$
float64 64 53 位元 約 15 至 16 位 $1.8 \times 10^{308}$ $5 \times 10^{-324}$

浮點數的精度

浮點數用有限個二進位有效位數來儲存數字。IEEE 754 把一個數寫成符號、指數與有效位數三部分,有效位數為 $p$ 位元時,

其中 $s \in \{0, 1\}$ 是符號,$b_i \in \{0, 1\}$ 是有效位數的第 $i$ 位,$n$ 是指數。第一位 $b_0$ 固定是 1,所以不必儲存,float64 實際只儲存 52 位,加上這個隱含的 1 就是 $p = 53$。也就是說,能被剛好表示的,只有用有限項 2 的負冪次湊得出來的數,例如 $0.625 = 2^{-1} + 2^{-3}$。

範例

以 $0.625$ 為例。先用 2 的負冪次湊出來,$0.625 = 2^{-1} + 2^{-3}$,也就是二進位的 $0.101_{2}$。把小數點移到第一個 1 的後面,得到 $1.01_{2} \times 2^{-1}$。

根據式 $\eqref{eq:float-value}$ 可知,各部分是

  • 符號 $s = 0$(正數)
  • 指數 $n = -1$
  • 有效位數 $b_0 = 1$(隱含)、$b_1 = 0$、$b_2 = 1$,之後全是 0

代入式 $\eqref{eq:float-value}$ 可得

只需要有限項,所以 0.625 存得下。可以直接看 float64 的 64 個位元:

1
2
3
fmt.Printf("%016x\n", math.Float64bits(0.625))

// 3fe4000000000000

前三位十六進位 3fe 包含符號 0 與指數欄位 $1022 = 1023 - 1$,所以 $n = -1$。後面的 4(二進位 0100)是小數部分的開頭,也就是 $b_1 = 0$、$b_2 = 1$,其餘全是 0。

根據式 $\eqref{eq:float-value}$ 可知,從 1 往上,下一個可表示的數是把最後一位 $b_{p-1}$ 設為 1,與 1 的間距是

這個間距越小,表示精度越高。float32 大約能準確到 7 位十進位數字,float64 約 15 至 16 位。

展開查看數學推導

若要換算成十進位,可以比較能區分的數量:$p$ 位元的二進位有 $2^p$ 種組合,$d$ 位的十進位有 $10^d$ 種,令兩者相等,即 $10^d = 2^p$,兩邊取 $\log_{10}$ 可得

範例

float32 的 $p = 24$,代入式 $\eqref{eq:float-eps}$ 與式 $\eqref{eq:float-digits}$ 可得

float64 的 $p = 53$,則是

這和用 math.Nextafter 實際量出的間距(1.1920929e-07 與 2.220446049250313e-16)一致。

兩者都存不下剛好的 0.1,只是 float64 近得多3:

1
2
3
4
5
6
var a float32 = 0.1
var b float64 = 0.1
fmt.Printf("%.20f\n%.20f\n", a, b)

// 0.10000000149011611938
// 0.10000000000000000555

這是因為 0.1 要用無限多項才湊得出來,而式 $\eqref{eq:float-value}$ 最多只加到 $i = p - 1$。

展開查看數學推導

先把 0.1 寫成 $1.\text{xxx} \times 2^{n}$ 的形式。$0.1 = 1.6 \times 2^{-4}$,所以只要把 $0.6$ 轉成二進位。每次乘 2 取整數部分,整數部分是 1,表示這一位的權重放得下,剩下的小數部分繼續乘 2:

步驟 乘 2 整數部分(這一位) 小數部分
1 $0.6 \times 2 = 1.2$ 1 0.2
2 $0.2 \times 2 = 0.4$ 0 0.4
3 $0.4 \times 2 = 0.8$ 0 0.8
4 $0.8 \times 2 = 1.6$ 1 0.6

第 4 步的小數部分又回到 $0.6$,所以 1001 會無限循環,不會結束:

float64 只保留小數點後 52 位,剛好是 1001 重複 13 次。被捨去的下一位起是 1001…,第一位是 1 且後面還有非零位,超過一半,所以捨入時進位,最後 4 位 1001 變成 1010。存起來的值因此比 0.1 大一點點,與上面印出的 0.10000000000000000555 一致。

可以直接看 float64 的 64 個位元:

1
2
3
fmt.Printf("%016x\n", math.Float64bits(0.1))

// 3fb999999999999a

前三位十六進位 3fb 包含符號 0 與指數欄位 $1019 = 1023 - 4$(欄位存的是 $n + 1023$),所以 $n = -4$。後面 13 位 999999999999a 就是小數部分:12 個 9(1001)加上進位後的 a(1010)。

精度不足還會造成加 1 沒有效果:float32 從 $2^{24}$ 開始、float64 從 $2^{53}$ 開始,相鄰兩數的間距大於 1,加 1 會被捨入回原來的數。

展開查看數學推導

數值落在 $2^k \leq x < 2^{k+1}$ 時,相鄰兩個可表示的數的間距是

float32 的 $p = 24$,當 $k = 24$,也就是從 $2^{24} = 16{,}777{,}216$ 開始,間距是 $2^{24-24+1} = 2$,大於 1,所以加 1 會被捨入回原來的數:

1
2
3
4
5
var c float32 = 16777216 // 2^24
var d float64 = 16777216
fmt.Println(c+1, d+1)

// 1.6777216e+07 1.6777217e+07

float64 的 $p = 53$,要到 $2^{53}$ 才會遇到同樣的情形。

通常使用 float64,是因為它的精度與範圍都夠用,而且 math 套件的函式(例如 math.Sqrt)接受的就是 float64。float32 比較適合資料量很大、需要省記憶體的情況。

字串

字串需要用雙引號 " 包起來,型別是 string。與 Python 不同的是,Go 的字串內容是以 UTF-84 編碼的位元組序列,而不是字元序列,所以 len 回傳的是位元組數:

1
2
3
4
s := "Go你好"
fmt.Println(len(s))

// 8

Python 中 len("Go你好") 是 4,在 Go 卻是 8。這是因為在 UTF-8 中,不同字元占用的位元組數不一樣:

字元 位元組數 範例
ASCII(英文、數字、半形符號) 1 G、o
常見的中文 3 你、好
emoji 4 😀

所以 Go你好 是 $1 + 1 + 3 + 3 = 8$ 個位元組。ASCII 一共只有 128 個字元,完整的對照可以參考圖 1。

ASCII 字元對照表
圖 1 ASCII 字元對照表,改自 Wikimedia Commons(公有領域)

用索引取值時,取出的同樣是位元組:

1
2
3
4
5
6
s := "Go你好"
fmt.Println(s[0], s[2])
fmt.Printf("%T\n", s[0])

// 71 228
// uint8

s[0] 是 G 的位元組 71,s[2] 則是 你 三個位元組中的第一個 228,並不是一個完整的字。型別 uint8 就是前面整數那一節的 0 到 255。

Go 把一個字元對應的編號稱為 rune,型別是 int32 的別名。要以字元為單位處理字串,例如計算字數,就需要用到它,這部分留到之後專門介紹字串的文章。

布林值

布林值的型別是 bool,只有 true 和 false 兩個值,而且都是小寫,與 Python 的 True、False 不同:

1
2
3
4
5
6
7
a := true
b := false
fmt.Println(a, b)
fmt.Printf("%T\n", a)

// true false
// bool

Python 的 True 其實就是整數 15,所以 True + 1 會得到 2。Go 的 bool 則完全不是數字,不能用 1 或 0 代替,也不能參與數值運算,連型別轉換都不行:

1
2
3
4
5
6
7
8
9
var b bool = 1
// cannot use 1 (untyped int constant) as bool value in variable declaration

a := true
fmt.Println(a + 1)
// invalid operation: a + 1 (mismatched types bool and untyped int)

fmt.Println(int(a))
// cannot convert a (variable of type bool) to type int

這三行都無法編譯。bool 與數值型別之間沒有任何轉換,這也呼應了前面 Go 偏好明確、不自動轉換的設計。

型別轉換與零值

前面介紹的幾種型別,實際使用時常常需要互相搭配。這一節說明型別之間如何轉換,以及變數宣告後沒有給值時,Go 會幫它填上什麼。

型別轉換

在 Python 中,3 * 1.5 會自動算出 4.5。但在 Go 裡,不同的數值型別不能直接混合運算 (The Go Project, 2026, Numeric types):

1
2
3
4
5
var a int = 3
var b float64 = 1.5
fmt.Println(a * b)

// invalid operation: a * b (mismatched types int and float64)

要先把其中一個轉成另一個的型別,語法是

1
型別(值)

以 3 與 1.5 為例,直觀的做法有兩種:把 1.5 轉成整數,或把 3 轉成浮點數。先試第一種:

1
2
3
4
5
var a int = 3
var b float64 = 1.5
fmt.Println(a * int(b))

// 3

但是 1.5 轉成 int 後會被截成 1,小數部分就不見了,算出來的是 3 * 1 = 3,不是我們要的 4.5。

所以要反過來,把整數轉成浮點數:

1
2
3
4
5
var a int = 3
var b float64 = 1.5
fmt.Println(float64(a) * b)

// 4.5

3 變成 3.0,沒有損失任何資訊。一般的原則是轉成能完整表示兩個數的型別:整數能表示的值,浮點數大多也能表示6,但整數表示不了 1.5。

為什麼 Go 不自動轉換?

自動轉換容易讓人搞不清楚運算式是不是無號、數值有多大、會不會溢位,以及結果在不同機器上是否一致。Go 選擇讓程式碼多寫一些明確的轉換,換取清楚與可移植 (The Go Project, n.d., Why does Go not provide implicit numeric conversions?)。同時,讀程式的人也能一眼看出哪裡發生了型別轉換,也就是哪裡可能損失資訊。

反方向,也就是把浮點數轉成整數時,小數部分會被捨去:

1
2
3
4
5
var f float64 = 3.9
var g float64 = -3.9
fmt.Println(int(f), int(g))

// 3 -3

這個規則叫做向零取整(round toward zero)(The Go Project, 2026, Conversions),也就是往零的方向截斷,不是四捨五入,也不是無條件捨去(取地板)。

展開查看數學推導

用數學式表示為

其中 $\operatorname{sgn}(x)$ 是 $x$ 的正負號。代入 $x = 3.9$ 得 $1 \times \lfloor 3.9 \rfloor = 3$;代入 $x = -3.9$ 得 $-1 \times \lfloor 3.9 \rfloor = -3$。對照取地板 $\lfloor -3.9 \rfloor = -4$,可以看出式 $\eqref{eq:trunc}$ 與取地板在負數時結果不同。

若需要四捨五入,要先用 math.Round 處理,再轉成整數。

最後,常數與變數的轉換規則不同。對常數而言,編譯器在編譯時就知道 3.9 不是整數,所以會直接拒絕:

1
2
3
fmt.Println(int(3.9))

// cannot convert 3.9 (untyped float constant) to type int

常數在編譯時檢查,變數在執行時處理,這個差別在前面的整數溢位已經出現過一次,在型別轉換又出現了一次。

零值

Python 的變數必須先賦值才能使用,但是 Go 的變數一宣告就有值,而如果沒有給值時,就是該型別的零值:

1
2
3
4
5
6
var n int
var s string
var ok bool
fmt.Printf("%d %q %t\n", n, s, ok)

// 0 "" false

零值的規則由型別決定,所有型別的零值如下 (The Go Project, 2026, The zero value):

型別 零值
bool false
數值型別(整數、浮點數、複數) 0
string ""(空字串)
指標、函式、interface、slice、channel、map nil
陣列、struct 每個元素或欄位各自取其型別的零值

因此 var i int 與 var i int = 0 完全等價。nil 留到講 slice 和 map 時再說明。

練習

下面的題目對應前面各節,每題都附有解答,預設是收起來的,先自己想過再展開。

預測輸出

題目敘述

題目 1:下面的程式會輸出什麼?

1
2
3
4
5
6
7
8
var a int8 = 127
a++

var b uint8
b--

x, y := 0.1, 0.2
fmt.Println(a, b, x+y == 0.3)
展開查看解答
1
// -128 255 false
  • a:int8 的 127 + 1 超出範圍,繞回 -128。
  • b:uint8 的 0 - 1 繞回 $-1 \bmod 2^8 = 255$。
  • x+y == 0.3:0.1 與 0.2 都存不下剛好的值,相加得到 0.30000000000000004,不是最接近 0.3 的那個數,所以是 false。
題目敘述

題目 2:下面的程式會輸出什麼?跟題目 1 的最後一項為什麼不同?

1
2
x, y := 0.5, 0.25
fmt.Println(x+y == 0.75)
展開查看解答
1
// true

0.5 = $2^{-1}$、0.25 = $2^{-2}$、0.75 = $2^{-1} + 2^{-2}$,都能用有限項 2 的負冪次湊出來,所以都存得下,相加也沒有誤差。0.1 和 0.2 需要無限多項,才會有題目 1 的結果。

題目敘述

題目 3:下面的程式會輸出什麼?

1
2
3
4
5
6
7
var (
a int
b float64
c string
d bool
)
fmt.Printf("%v %v %q %v\n", a, b, c, d)
展開查看解答
1
// 0 0 "" false

沒有給值的變數會是零值:數字型別是 0,字串是空字串,布林值是 false。

題目敘述

題目 4:下面的程式會輸出什麼?為什麼?

1
2
3
a := "hi"
b := "你好"
fmt.Println(len(a), len(b))
展開查看解答
1
// 2 6

len 回傳的是位元組數,不是字元數。UTF-8 中英文字母佔 1 個位元組,一個中文字佔 3 個位元組,所以 你好 是 $2 \times 3 = 6$。

題目敘述

題目 5:下面的程式會輸出什麼?怎麼算出來的?

1
2
3
var a uint8 = 200
var b uint8 = 100
fmt.Println(a + b)
展開查看解答
1
// 44

a、b 是變數,編譯器在編譯時不知道它們相加的結果,所以可以編譯,執行時才繞回。uint8 有 $2^8 = 256$ 種值,根據式 $\eqref{eq:wrap-unsigned}$ 可得 $300 \bmod 256 = 44$。

修正程式

題目敘述

題目 6:下面的程式能編譯嗎?不能的話,有哪兩種改法?各自輸出什麼?

1
2
3
var a int = 10
var b float64 = 2.5
fmt.Println(a * b)
展開查看解答

不能編譯。int 與 float64 是不同型別,Go 不會自動轉換:

1
// invalid operation: a * b (mismatched types int and float64)

改法一,把 a 轉成 float64:

1
2
3
fmt.Println(float64(a) * b)

// 25

改法二,把 b 轉成 int,小數部分會被丟掉(2.5 變成 2):

1
2
3
fmt.Println(a * int(b))

// 20
題目敘述

題目 7:下面的程式想交換 x 和 y,能編譯嗎?不能的話,怎麼改?

1
2
3
4
x := 10
y := 20
x, y := y, x
fmt.Println(x, y)
展開查看解答

不能編譯,:= 左邊至少要有一個新變數,這裡 x、y 都已存在:

1
// no new variables on left side of :=

把 := 改成 =。等號右邊會先全部算好,再一起賦值給左邊,所以不需要暫存變數:

1
2
3
4
x, y = y, x
fmt.Println(x, y)

// 20 10
題目敘述

題目 8:下面這行能編譯嗎?為什麼結果跟題目 5 不同?

1
var c uint8 = 200 + 100
展開查看解答

不能編譯:

1
// cannot use 200 + 100 (untyped int constant 300) as uint8 value in variable declaration (overflows)

200 + 100 是常數運算式,編譯器在編譯時就算得出是 300,確定 uint8 存不下,所以直接報錯。題目 5 的 a + b 是變數,要到執行時才知道結果,只能繞回 44。

實作

題目敘述

題目 9:身高 170(公分)、體重 65(公斤),兩個都先宣告成 int。算出 BMI 並印出,保留 1 位小數。預期輸出是 22.5。

【註】BMI = 體重(公斤)÷ 身高(公尺)的平方。

展開查看解答
1
2
3
4
5
6
7
8
height := 170
weight := 65

m := float64(height) / 100
bmi := float64(weight) / (m * m)
fmt.Printf("%.1f\n", bmi)

// 22.5

要先轉型再除。如果寫成 float64(height / 100),height / 100 兩邊都是 int,整數除法會先得到 1,小數部分已經丟掉了,後面轉成 float64 也救不回來。

小結

這篇介紹了 Go 的兩種變數宣告方式 var 與 :=,以及四種基本型別:整數、浮點數、字串與布林值。Go 是靜態型別的語言,不同型別之間不會自動轉換,必須明確寫出轉換,而沒有給值的變數則會得到該型別的零值。下一篇我們將會介紹運算子~

1. 有號整數在電腦中以二補數表示,溢位時只會保留最低的 $n$ 個位元,所以結果會落在範圍的另一端。 ↩
2. IEEE 754 是浮點數運算的國際標準,規定了浮點數的儲存格式與捨入方式,大多數程式語言與處理器都遵循它。 ↩
3. fmt.Printf 依照格式字串輸出。%.20f 是印出小數點後 20 位的浮點數,\n 是換行;後面還會用到 %T(型別)、%d(整數)、%q(加上引號的字串)與 %t(布林值)。完整的格式請見 fmt 套件文件。 ↩
4. Unicode 為每個字元訂定一個編號,UTF-8 則規定這個編號要怎麼存成 1 到 4 個位元組。 ↩
5. 更精確地說,Python 的 bool 是 int 的子類別,True == 1、False == 0。 ↩
6. 更精確地說,由式 $\eqref{eq:float-gap}$ 可知,float64 的 $p = 53$,當 $k \leq 52$ 時,相鄰兩數的間距 $2^{k-52}$ 不大於 1,所以 $2^{53}$ 以內的整數都能被 float64 精確表示,轉換不會損失資訊。超過 $2^{53}$ 的整數就不一定能精確表示了。 ↩

參考文獻

The Go Project (2026, May 26). The Go Programming Language Specification (Language version go1.27). The Go Programming Language. https://go.dev/ref/spec

The Go Project (n.d.). Frequently Asked Questions (FAQ). The Go Programming Language. https://go.dev/doc/faq