【Go】Go言語で指数表記の文字列型を数値型にパースする。
指数表記の文字列型???何それ?
これ。
package main import ( "fmt" ) func main() { fmt.Println(float64(1000000)) //-> 1e+06 }
goで浮動小数点数型をデフォルトではフォーマットした時に 1e+06
のように出力され、こういう表記を指数表記、英語ではexponential notation
、notation
、scientific notation
とか呼ぶ。
それがどうしたの?
って感じだろうが少し待って欲しい、例えば以下のようなreturnScientificNotation関数
的な何かでこの文字列を返されてしまった時、でも自分はその数値を int
として使いたい場合はどうしようってのが今回の話題だ(こんな事起こるの?って感じだが僕の場合、jwtのtokenでこれが起きた)。
package main import ( "fmt" "reflect" ) func returnScientificNotation() string{ // なんかの処理 return "1e+06" } func main() { str := returnScientificNotation() // 1000000が欲しいのになぜか指数表記の文字列型で返された dst := doSomeThing(str) // なんかやって元のint型に直したい if reflect.DeepEqual(1000000, dst) { fmt.Println("same") } else { fmt.Println("different") } }
それの何が問題なの?
実は結構詰んでいる。
例えば文字列->整数は普通こういうパースは strconv.Atoi
パッケージを使うのが定石だが、当然エラーする。
package main import ( "fmt" "strconv" ) func main() { dst, err := strconv.Atoi("1e+06") if err != nil { panic(err) } fmt.Println(dst) }
panic: strconv.Atoi: parsing "1e+06": invalid syntax goroutine 1 [running]: main.main() /tmp/sandbox701385143/main.go:13 +0x180
それっぽい strconv
のモジュールもない。
interfaceの型のキャストもだめ。
package main import ( "fmt" ) func main() { var str interface{} str = "1e+06" dst, ok := str.(int) if !ok { panic("cannot cast") } fmt.Println(dst) }
panic: cannot cast goroutine 1 [running]: main.main() /tmp/sandbox399888292/main.go:14 +0xc0
どうしよ。
こうしよう。
package main import ( "fmt" "strconv" "reflect" ) func parseScientificNotation(str string) (int, error) { float, err := strconv.ParseFloat(str, 64) if err != nil { return 0, err } return int(float), nil } func returnScientificNotation() string{ // なんかの処理 return "1e+06" } func main() { str := returnScientificNotation() // 1000000が欲しいのになぜか指数表記の文字列型で返された dst, _ := parseScientificNotation(str) // なんかやって元のint型に直したい if reflect.DeepEqual(1000000, dst) { fmt.Println("same") // -> same } else { fmt.Println("different") } }
かいせつ
ポイントは strconv.ParseFloat
で string -> float64をした後で int型にキャストしている所で、
strconv.ParseFloat
は実は "0.000001"
のような表記の他、今回のような指数表記の文字列型にも対応している。
今回は冒頭で指数表記の文字列が float64()
のマクロでキャストをしているので、指数表記の文字列がfloat系の型からキャストされた後に出力されてるから・・・と予想できやすいが、いざライブラリとかでいきなり 1e+06
と返されるとこの答えにまず行き着きにくい(という言い訳・・・・w)
ソースではここのファイルの genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte
関数を見ればパースの動きがわかるだろう。
感想
まじで fmt.Sprint(float64(int型))
とかで返してくるライブラリやめろ。