通过循环求余实现。进制的转换也是这种方式。
for u >= b {
i--
a[i] = uint(u)&m
u >>= s
}
上面的代码实现了进制的转换。而 digits[uint(u)&m] 实现了转换后的结果再转成字符。
常规情况
b := uint64(base)
for u >= b {
i--
q := u / b
a[i] = digits[uint(u-q*b)]
u = q
}
// u < base
i--
a[i] = digits[uint(u)]
依然是循环求余来实现。这段代码更像是给人看的。和上面2的倍数的进制转换的区别在于,上面的代码把除法 / 换成了右移( >> ) s 位,把求余 % 换成了逻辑与 & 操作。
Sprintf 的实现
switch f := arg.(type) {
case bool:
p.fmtBool(f, verb)
case float32:
p.fmtFloat(float64(f), 32, verb)
case float64:
p.fmtFloat(f, 64, verb)
case complex64:
p.fmtComplex(complex128(f), 64, verb)
case complex128:
p.fmtComplex(f, 128, verb)
case int:
p.fmtInteger(uint64(f), signed, verb)
...
}
判断类型,如果是整数 int 类型,不需要反射,直接计算。支持的都是基础类型,其它类型只能通过反射实现。
Sprintf 支持的进制只有10 %d 、16 x 、8 o 、2 b 这四种,其它的会包 fmt: unknown base; can't happen 异常。
switch base {
case 10:
for u >= 10 {
i--
next := u / 10
buf[i] = byte('0' + u - next*10)
u = next
}
case 16:
for u >= 16 {
i--
buf[i] = digits[u&0xF]
u >>= 4
}
case 8:
for u >= 8 {
i--
buf[i] = byte('0' + u&7)
u >>= 3
}
case 2:
for u >= 2 {
i--
buf[i] = byte('0' + u&1)
u >>= 1
}
default:
panic("fmt: unknown base; can't happen")
}
2、8、16进制和之前 FormatInt 差不多,而10进制的性能差一些,每次只能处理一位数字,而不像 FormatInt 一次处理两位。
性能对比
var smallInt = 35
var bigInt = 999999999999999
func BenchmarkItoa(b *testing.B) {
for i := 0; i < b.N; i++ {
val := strconv.Itoa(smallInt)
_ = val
}
}
func BenchmarkItoaFormatInt(b *testing.B) {
for i := 0; i < b.N; i++ {
val := strconv.FormatInt(int64(smallInt), 10)
_ = val
}
}
func BenchmarkItoaSprintf(b *testing.B) {
for i := 0; i < b.N; i++ {
val := fmt.Sprintf("%d", smallInt)
_ = val
}
}
func BenchmarkItoaBase2Sprintf(b *testing.B) {
for i := 0; i < b.N; i++ {
val := fmt.Sprintf("%b", smallInt)
_ = val
}
}
func BenchmarkItoaBase2FormatInt(b *testing.B) {
for i := 0; i < b.N; i++ {
val := strconv.FormatInt(int64(smallInt), 2)
_ = val
}
}
func BenchmarkItoaBig(b *testing.B) {
for i := 0; i < b.N; i++ {
val := strconv.Itoa(bigInt)
_ = val
}
}
func BenchmarkItoaFormatIntBig(b *testing.B) {
for i := 0; i < b.N; i++ {
val := strconv.FormatInt(int64(bigInt), 10)
_ = val
}
}
func BenchmarkItoaSprintfBig(b *testing.B) {
for i := 0; i < b.N; i++ {
val := fmt.Sprintf("%d", bigInt)
_ = val
}
}









