七叶笔记 » golang编程 » Go 中类型转换的那些事

Go 中类型转换的那些事

strconv包可以大致分成string/数字型转换,rune/sting转换。常用的string/数字型转换又可以分三大类,Parse/Format/Append,简单理解就是数字型解析成字符串、数字型格式化成字符串和数字型拼接三种方法。

Parse

Parse系列函数


输入输出
ParseBools stringbool,error ParseInts string, 基数 int, 位数 intint64,error ParseUints string, 基数 int, 位数 intint64,error ParseFloats string, 位数 intfloat64, error ParseComplexs string, 位数 intcomplex128, error

其中ParseInt()/ParseUint()在类型转换一中有介绍过主要流程,其他流程大同小异。

ParseBool

将字符串转换成布尔类型,因为bool结果只有两个,需要转换的目标也有限,所以这个函数超简单,直接枚举长的像bool型的字符串,对应到bool结果上。不过“t”/”f”也能进行映射成true/false 是第一次见。

 func ParseBool(str string) (bool, error) {
 switch str {
 case "1", "t", "T", "true", "TRUE", "True":
  return true, nil
 case "0", "f", "F", "false", "FALSE", "False":
  return false, nil
 }
 return false, syntaxError("ParseBool", str)
}
  

ParseFloat

ParseFloat稍微复杂点,不过也是一样的套路:检查系统位数->检查极限字符->拆分字符串->遍历字符串
字符串“1.1”大致流程图


ParseFloat首先选择32/64位操作,32位转换和64位转换差别不大。以64位为例:具体使用内部函数atof64()里面有三个重要函数,分别是special()名如其功能,就是检查是否存在特殊情况比如无穷或非数,但它只能识别infinity/inf/nan;

readFloat()将字符串拆解成mantissa uint64 尾数数, exp int指, neg 是否负数, trunc 是否溢出, hex bool是否十六进制, i int 占用字节数, ok bool 转换是否成功。有了这些后面就可以直接处理了。
如果经过readFloat拆解是十六进制,则使用atofHex() 如果不是就可能使用atof64exact()和eiselLemire64算法,对于‘1.1’ atof64exact()就够了,atof64exact()也不复杂使用float64对尾数进行转换,对指数进行处理再与尾数相乘即可。

顺便看下16进制浮点数如何转换10进制浮点的,比如‘0x1a.2p1’ 0x是16进制标识,1a.2是尾数,p1就是e1。先将1a.2转换成二进制11010.001 指数是1小数点就向右移一位110100.01,再将2进制转换成10进制,110100=>52 .01 =>.25 52.25 atofHex()其实就是做的类似事情(ParseFloat()对16进制处理时候在readFloat()对p做了单独识别,而且p是判断16进制浮点数的条件。)

  v := "1.1"
 if s, err := strconv.ParseFloat(v, 32); err == nil {
    fmt.Printf("%T, %v\n", s, s)
 }
 //float64, 1.100000023841858  #ParseFloat输出是float64所以按32位处理会影响精度
 if s, err := strconv.ParseFloat(v, 64); err == nil {
    fmt.Printf("%T, %v\n", s, s)
 }
    //float64, 1.1
 v := "0xD.2p1"
 if s, err := strconv.ParseFloat(v, 64); err == nil {
    fmt.Print(s)
 }
 //26.25
  

ParseComplex

ParseXXX系列还有一个复数转换,复数不怎么用,常见用在几何或者物理方面。好在ParseComplex不复杂,基本就是parseFloatPrefix处理字符串,也就是ParseFloat()执行两次 。ParseComplex返回complex128的复数,需要注意是ParseComplex(s string, bitSize int) bitSize是complex的位数是64/128,函数默认是采用parseFloatPrefix(s,64)处理也就是atof64(s),所以处理complex64一定要指定bitSize对应目标位数,不然处理浮点型实虚数时会有精度缺失问题

  v := "1.1+1i"
 c1 ,_:= strconv.ParseComplex(v, 128)
 fmt.Print(c1)
 //(1.1+1i)

 c2 ,_:= strconv.ParseComplex(v, 64)
 fmt.Print(c2)
 //(1.100000023841858+1i)
  

Format

strconv包里还有另一类函数,FormatXX 将数字转换成字符串


输入输出
FormatBoolb boolstring FormatInti int64, 基数 intstring FormatUinti uint64,基数 intstring FormatFloatf float64, fmt byte, prec, bitSize intstring FormatComplexc complex128, fmt byte, prec, bitSize intstring

FormatBool()很简单,就是直接返回“true/false”字符串;

 func FormatBool(b bool) string {
    if b {
          return "true"
     }
     return "false"
}

  

FormatInt()和FormatUint()上次也学习过了Atoi

FormatFloat

FormatFloat(),需要四个参数f float64, fmt byte 格式参数(fmt必须是byte,所以用单引号), prec 小数保留位数(如-1是全保留), bitSize int基数
fmt可供选择:

 // The format fmt is one of
// 'b' (-ddddp±ddd, a binary exponent), 二进制表达式
// 'e' (-d.dddde±dd, a decimal exponent),   十进制表达式
// 'E' (-d.ddddE±dd, a decimal exponent),
// 'f' (-ddd.dddd, no exponent),    普通的浮点表达式
// 'g' ('e' for large exponents, 'f' otherwise),    大数情况使用的标识
// 'G' ('E' for large exponents, 'f' otherwise),
// 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or 十六进制表达式
// 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent).
  

FormatFloat使用内部函数genericFtoa() ,genericFtoa()做的事情就是根据参数计算出原始float的十进制表达式,fmt是格式参数,指定计数格式,并且这个参数直接取决后续计算过程。
精度参数prec小数点位置(如果是-1默认全部长度),而且这时候使用Grisu3算法计算浮点数,据说是普通精度算法的四倍,具体如何计算暂时没有了解。
如果指定了精度长度就用普通的精度算法计算。篇幅限制就不展开genericFtoa()函数;

FormatComplex()和ParseComplex()一样,相当于执行两次FormatFloat()

  var v float64
 v = 3.1415926535

 s64 := strconv.FormatFloat(v, 'E', 2, 64)
 fmt.Printf("%T, %v\n", s64, s64)
 //string, 3.14E+00
 
 v:= (1.1+1i)
 fmt.Print(strconv.FormatComplex(v,'f',-1,64))
 //(1.1+1i)
  

Append

最后一类AppendXX函数,将数字类型进行拼接,需要注意不是计算,最终输出数组。因为最终拼接结果是字符串数组,所以拼接前需要和FormatXX一致的转换过程,如AppendFloat()底层处理逻辑就与FormatFloat()是相同逻辑

总结

  1. strconv包有三类函数 ParseXX 将字符串转换成数字类型;FormatXX 将数字类型转换成字符串 ;AppendXX 数字类型的拼接函数。这三类函数解决常见转换问题;
  2. 精度问题 strconv转换函数大多默认返回float64,会导致输入float32的结果精度失真,使用前应注意;
  3. strconv还有处理rune/ASCII/图形符号之间的转换QuoteXX系列,以及rune/图形判断函数,逻辑也都不复杂,可以直接使用;

相关文章