GO中高效的將int轉(zhuǎn)換為string的方法與源碼
Go 語(yǔ)言 中,將整數(shù)(int)轉(zhuǎn)換為字符串(string)是一項(xiàng)常見(jiàn)的操作。
本文將從逐步介紹幾種在 Go 中將 int 轉(zhuǎn)換為 string 的常見(jiàn)方法,并重點(diǎn)剖析這幾種方法在性能上的特點(diǎn)。另外,還會(huì)重點(diǎn)介紹 FormatInt 高效的算法實(shí)現(xiàn)。

使用 strconv.Itoa
最直接且常用的方法是使用 strconv 包中的 Itoa 函數(shù)。Itoa 是 “Integer to ASCII” 的簡(jiǎn)寫,它提供了一種快速且簡(jiǎn)潔的方式實(shí)現(xiàn)整數(shù)到字符串之間的轉(zhuǎn)換。
示例代碼如下:
package main
import (
"strconv"
"fmt"
)
func main() {
i := 123
s := strconv.Itoa(i)
fmt.Println(s)
}
strconv.Itoa 是通過(guò)直接將整數(shù)轉(zhuǎn)換為其 ASCII 字符串表示形式。這個(gè)過(guò)程中盡量減少了額外的內(nèi)存分配,沒(méi)有復(fù)雜邏輯。
使用 fmt.Sprintf
另一種方法是,使用 fmt 包的 Sprintf 函數(shù)。這個(gè)方法在功能上更為強(qiáng)大和靈活,因?yàn)樗芴幚砀鞣N類型并按照指定的格式輸出。
示例代碼如下:
package main
import (
"fmt"
)
func main() {
i := 123
s := fmt.Sprintf("%d", i)
fmt.Println(s)
}
雖然 fmt.Sprintf 在功能上非常強(qiáng)大,但它的性能通常不如 strconv.Itoa。
為什么呢?
因?yàn)?fmt.Sprintf 內(nèi)部使用了反射(reflection)確定輸入值類型,并且在處理過(guò)程中涉及到更多的字符串拼接和內(nèi)存分配。
使用 strconv.FormatInt
當(dāng)需要更多控制或處理非 int 類型的整數(shù)(如 int64)時(shí),可以使用 strconv 包的 FormatInt 函數(shù)。
package main
import (
"strconv"
"fmt"
)
func main() {
var i int64 = 123
s := strconv.FormatInt(i, 10) // 10 表示十進(jìn)制
fmt.Println(s)
}
strconv.FormatInt 提供了對(duì)整數(shù)轉(zhuǎn)換過(guò)程的更細(xì)粒度控制,包括 base 的選擇(例如,十進(jìn)制、十六進(jìn)制等)。
與 strconv.Itoa 類似,FormatInt 在性能上也非常可觀,而且 FormatInt 提供了既靈活又高效的解決方案。
如果我們查看 strconv.Itoa 源碼,會(huì)發(fā)現(xiàn) strconv.Itoa 其實(shí)是 strconv.FormatInt 的一個(gè)特殊情況。
// Itoa is shorthand for FormatInt(int64(i), 10).
func Itoa(i int) string {
return FormatInt(int64(i), 10)
}
現(xiàn)在 int 轉(zhuǎn) string 的高性能源碼剖析,就變成了重點(diǎn)剖析 FormatInt。
FormatInt 深入剖析
基于 Go 1.21 版本的 itoa.go 源碼,我們可以深入理解 strconv 包中整數(shù)到字符串轉(zhuǎn)換函數(shù)的高效實(shí)現(xiàn)。
func FormatInt(i int64, base int) string {
if fastSmalls && 0 <= i && i < nSmalls && base == 10 {
return small(int(i)) // 100 以內(nèi)的十進(jìn)制小整數(shù),使用 small 函數(shù)轉(zhuǎn)化
}
_, s := formatBits(nil, uint64(i), base, i < 0, false) // 其他情況使用 formatBits
return s
}
以下是對(duì)其核心部分的詳細(xì)解讀,將會(huì)突出了其性能優(yōu)化的關(guān)鍵方面,結(jié)合具體的源碼實(shí)現(xiàn)說(shuō)明。

1. 快速路徑處理小整數(shù)
對(duì)于常見(jiàn)的小整數(shù),strconv 包提供了一個(gè)快速路徑,small 函數(shù),直接返回預(yù)先計(jì)算好的字符串,避免了運(yùn)行時(shí)的計(jì)算開(kāi)銷。
func small(i int) string {
if i < 10 {
return digits[i : i+1]
}
return smallsString[i*2 : i*2+2]
}
對(duì)于小于 100 的十進(jìn)制整數(shù),采用這個(gè)快速實(shí)現(xiàn)方案,或許這也是整數(shù)轉(zhuǎn)字符串的最常見(jiàn)使用場(chǎng)景吧。
small 函數(shù)通過(guò)索引到 smallsString 和 digits 獲取小整數(shù)的字符串表示,這個(gè)過(guò)程非??焖?。 digits 和 smallsString 的值,如下所示:
const smallsString = "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899" const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
它們也就是十進(jìn)制 0-99 與對(duì)應(yīng)字符串的映射。
2. formatBits 函數(shù)的高效實(shí)現(xiàn)
FormatInt 最復(fù)雜的部分是 formatBits 函數(shù),它是整數(shù)到字符串轉(zhuǎn)換的核心,它針對(duì)不同的基數(shù)進(jìn)行了優(yōu)化。

10進(jìn)制轉(zhuǎn)換的優(yōu)化
對(duì)于10進(jìn)制轉(zhuǎn)換,formatBits 使用了基于除法和取余的算法,并通過(guò) smallsString 加速兩位數(shù)的字符串獲取。
if base == 10 {
// ... (32位系統(tǒng)的優(yōu)化)
us := uint(u)
for us >= 100 {
is := us % 100 * 2
us /= 100
i -= 2
a[i+1] = smallsString[is+1]
a[i+0] = smallsString[is+0]
}
// ... (處理剩余的數(shù)字)
}
- 對(duì)于 32 位系統(tǒng),使用32位操作處理較大的數(shù)字,減少 64 位除法的開(kāi)銷。
- 每次處理兩位數(shù)字,直接從
smallsString獲取對(duì)應(yīng)的字符,避免了單獨(dú)轉(zhuǎn)換每一位的開(kāi)銷。
2的冪基數(shù)的優(yōu)化
對(duì)于基數(shù)是2的冪的情況,formatBits 使用了位操作來(lái)優(yōu)化轉(zhuǎn)換。
} else if isPowerOfTwo(base) {
shift := uint(bits.TrailingZeros(uint(base))) & 7
b := uint64(base)
m := uint(base) - 1 // == 1<<shift - 1
for u >= b {
i--
a[i] = digits[uint(u)&m]
u >>= shift
}
// u < base
i--
a[i] = digits[uint(u)]
}
- 位操作是直接在二進(jìn)制上進(jìn)行,比除法和取余操作更快。
- 利用 2 的冪基數(shù)的特性,通過(guò)移位和掩碼操作獲取數(shù)字的各個(gè)位。
通用情況的處理
對(duì)于其他基數(shù),formatBits 使用了通用的算法,但仍然盡量減少了除法和取余操作的使用。
} else {
// general case
b := uint64(base)
for u >= b {
i--
// Avoid using r = a%b in addition to q = a/b
// since 64bit division and modulo operations
// are calculated by runtime functions on 32bit machines.
q := u / b
a[i] = digits[uint(u-q*b)]
u = q
}
我覺(jué)得最核心的算法就是利用移位和特殊路徑預(yù)置映射關(guān)系。另外,由于算法足夠優(yōu)秀,還避免了一些不必要內(nèi)存分配。
結(jié)論
將 int 轉(zhuǎn)化為 string 是一個(gè)非常常見(jiàn)的需求。Go 語(yǔ)言的 strconv 包中的 int 到 string 的轉(zhuǎn)換函數(shù)展示了 Go 標(biāo)準(zhǔn)庫(kù)對(duì)性能的深刻理解和關(guān)注。
通過(guò)快速處理小整數(shù)、優(yōu)化的 10 進(jìn)制轉(zhuǎn)換算法、以及2^n 基數(shù)的特別處理,這些函數(shù)能夠提供高效且穩(wěn)定的性能。這些優(yōu)化確保了即使在大量數(shù)據(jù)或在性能敏感的場(chǎng)景中,strconv 包的函數(shù)也能提供出色的性能
以上就是GO中高效的將int轉(zhuǎn)換string的方法與源碼的詳細(xì)內(nèi)容,更多關(guān)于GO中int轉(zhuǎn)換string的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
K8s部署發(fā)布Golang應(yīng)用程序的實(shí)現(xiàn)方法
本文主要介紹了K8s部署發(fā)布Golang應(yīng)用程序的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07
淺談Golang 切片(slice)擴(kuò)容機(jī)制的原理
我們知道 Golang 切片在容量不足的情況下會(huì)進(jìn)行擴(kuò)容,擴(kuò)容的原理是怎樣的呢,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
使用Go語(yǔ)言實(shí)現(xiàn)接口繼承的方式
在Go語(yǔ)言中,接口(interface)是一種定義方法集合的類型,它并不包含方法的具體實(shí)現(xiàn),只是規(guī)定實(shí)現(xiàn)該接口的類型必須提供這些方法的實(shí)現(xiàn),下面我將通過(guò)示例代碼來(lái)詳細(xì)解釋如何使用Go語(yǔ)言實(shí)現(xiàn)接口組合,以及為什么這種方式可以看作是實(shí)現(xiàn)接口繼承的一種方式2024-05-05
go語(yǔ)言中fallthrough的用法說(shuō)明
這篇文章主要介紹了go語(yǔ)言中fallthrough的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
Golang在Window環(huán)境使用Imagick7的過(guò)程
這篇文章主要介紹了Golang在Window環(huán)境使用Imagick7的過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11

