備忘として、Go 言語の「データ型」と「型変換(キャスト)」についてまとめておきます。
1.Go 言語データ型一覧
データ型 | サイズ | 説明 | 値の範囲 |
---|---|---|---|
uint8 | 1byte | 符号無し整数 | 0 ~ 255 |
uint16 | 2byte | 符号無し整数 | 0 ~ 65535 |
uint32 | 4byte | 符号無し整数 | 0 ~ 4294967295 |
uint64 | 8byte | 符号無し整数 | 0 ~ 18446744073709551615 |
int8 | 1byte | 符号付き整数 | -128 ~ 127 |
int16 | 2byte | 符号付き整数 | -32768 ~ 32767 |
int32 | 4byte | 符号付き整数 | -2147483648 ~ 2147483647 |
int64 | 8byte | 符号付き整数 | -9223372036854775808 ~ 9223372036854775807 |
float32 | 4byte | 単精度浮動小数点数 | 有効桁数6桁の実数 |
float64 | 8byte | 倍精度浮動小数点数 | 有効桁数14桁の実数 |
complex64 | 8byte | 複素数 | float32の実数部と虚数部を持つ複素数 |
complex128 | 16byte | 複素数 | float64の実数部と虚数部を持つ複素数 |
uint | 4byte 8byte |
符号無し整数 | uint32 または uint64 と同じ |
int | 4byte 8byte |
符号付き整数 | int32 または int64 と同じ |
uintptr | (8byte) | ポインタ型 | PC環境により異なる |
byte | 1byte | uint8のエイリアス | 0 ~ 255 |
rune | 4byte | int32のエイリアス | Unicodeのコードポイント1文字分を格納 |
string | - | 文字列型 | |
bool | 1byte | 論理値型 | true / false |
<出典>
The Go Programming Language Specification
(サイズについての補足)
データ型に bit 数が表示されているもについては、サイズは自明です。
それ以外のデータ型のサイズを、手元の環境(Windows 64bit)で確認した結果は次のとおりでした。
package main
import (
"fmt"
"unsafe"
)
func main() {
var ui uint
fmt.Printf("uint=%dbyte\n", unsafe.Sizeof(ui))
var i int
fmt.Printf("int=%dbyte\n", unsafe.Sizeof(i))
var p uintptr
fmt.Printf("uintptr=%dbyte\n", unsafe.Sizeof(p))
var r rune
fmt.Printf("rune=%dbyte\n", unsafe.Sizeof(r))
var b bool
fmt.Printf("bool=%dbyte\n", unsafe.Sizeof(b))
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
uint=8byte
int=8byte
uintptr=8byte
rune=4byte
bool=1byte
今は、ほとんどの PC が 64bit なので、大体同じ結果になるものと思います。
2.Go 言語のキャスト
キャストも簡単です。
int32 にキャストする場合は int32(キャストする変数)
float32 にキャストする場合は float32(キャストする変数)
というルールで書きます。
実際にコードで確認すると次のような感じです。
package main
import "fmt"
func main() {
// int64 → int32
var num64 int64 = 2147483647
var num32 int32 = int32(num64)
fmt.Printf("%T, %d\n", num32, num32) // int32, 2147483647
// uint16 → uint64
var unum16 uint16 = 65535
var unum64 uint64 = uint64(unum16)
fmt.Printf("%T, %d\n", unum64, unum64) // uint64, 65535
// float64 → float32
var f64 float64 = 123456.789876
var f32 float32 = float32(f64)
fmt.Printf("%T, %f\n", f32, f32) // float32, 123456.789062
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
int32, 2147483647
uint64, 65535
float32, 123456.789062
float64 から float32 にキャストすると、123456.789876 という数値が 123456.789062 というように、精度が落ちていることが確認できます。
2-1. 浮動小数点数から整数へのキャスト
浮動小数点数から整数型にキャストすると、小数点以下は切捨てとなるようです(以下のとおり)。
package main
import "fmt"
func main() {
// float32 → int32
var d32 float32 = 123.789
var num32 int32 = int32(d32)
fmt.Printf("%T, %d\n", num32, num32) // int32, 123
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
int32, 123
2-2. 符号あり変数と符号無し変数間のキャスト
uint8
(符号無し整数)の値の範囲は 0 ~ 255 です。
int8
(符号付き整数)の値の範囲は -128 ~ 127 です。
範囲の重複する 0 ~ 127 であれば、問題なくキャストできます。
範囲が重複しない 255
という数値が変数に格納されている場合に、キャストをすると次のようになります。
package main
import "fmt"
func main() {
// uint8 → int8
var unum8 uint8 = 255
var num8 int8 = int8(unum8)
fmt.Printf("%T, %d\n", num8, num8) // int8, -1
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
int8, -1
uint8
型の変数に入れている 255
という数値は、本来 int8
ではオーバーフローになるはずですが、エラーは起きず -1
という値で出力されています。
255
と -1
は値は見た目は違いますが、2進数にするとどちらも 11111111
を表しているため値自体は同一ということになります。
細かい説明は省略しますが、コンピュータで負の数を表す場合には、以下のように、その数値(2進数)に対応する**2の補数**が使用されるため、-1
は2進数で 11111111
と表現されるためです。
<-1 を二進数で表示する>
0000 0001 : 二進数で[1]を表示する
1111 1110 : 0000 0001 の1の補数
1111 1111 : 0000 0001 の2の補数(これが[-1]を表す)
<-5 を二進数で表示する>
0000 0101 : 二進数で[5]を表示する
1111 1010 : 0000 0101 の1の補数
1111 1011 : 0000 0101 の2の補数(これが[-5]を表す)
2-3. オーバーフローする場合のキャスト
元のサイズよりも小さいサイズのデータ型にキャストする場合には、オーバーフローとなる場合があります。
しかし、この場合もエラーは生じません。
package main
import "fmt"
func main() {
// uint16 → uint8
var unum16 uint16 = 258
var unum8 uint8 = uint8(unum16)
fmt.Printf("%T, %d\n", unum8, unum8) // uint8, 2
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
uint8, 2
uint16
では 258
であった変数を、uint8
にキャストすると 2
となります。
これは、キャストの結果により、下8桁分だけの値が取得されているためです。
<uint16に格納された値>
0000 0001 0000 0010 : 二進数で[258]を表示
<uint8に格納された値>
0000 0010 : uint8 へのキャストにより下8桁(8bit分)のみ取得される
結果として[2]が表示される。
3.文字列と数値の相互変換
「文字列から数値に変換する場合」及び「数値から文字列に変換する場合」は、strconv
ライブラリを使用します。
3-1. 文字列から数値への変換
3-1-1. 一般的な変換
文字列から数値への変換は、Atoi
関数を使用します(atoi とは「ASCII to Integer」の意です)。
コードを書くと、次のようになります。
package main
import (
"fmt"
"strconv"
)
func main() {
var numStr1 string = "2468"
i, err := strconv.Atoi(numStr1)
fmt.Println(i) // 2468
fmt.Println(err) // <nil>
var numStr2 string = "2468yen" // 数字以外の文字が混じっている場合
j, err := strconv.Atoi(numStr2)
fmt.Println(j) // 0
fmt.Println(err) // strconv.Atoi: parsing "2468yen": invalid syntax
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
2468
<nil>
0
strconv.Atoi: parsing "2468yen": invalid syntax
Atoi 関数を使用すると2つの値が戻ってきます。
1つ目は、int 型で文字列を変換した数値が返されます。
2つ目は、エラーの内容が返されます。
数値以外の文字を変換しようとすると、エラーが生じます。
上記のコードで、"2468yen"
という文字列を変換しようとすると、strconv.Atoi: parsing "2468yen": invalid syntax
という構文エラーが表示され、数値としては 0
という値が返されています。
エラーの取得が不要な場合は、次のように err
を _
に置き換えます。
package main
import (
"fmt"
"strconv"
)
func main() {
var numStr string = "12345678"
var n int // あらかじめ変数を指定する場合はint型
n, _ = strconv.Atoi(numStr)
fmt.Println(n) // 12345678
}
なお、数値は int 型で返されますので、あらかじめ変数を指定する場合は int 型の変数としておきます。
3-1-2. 詳細な変換指定
package main
import (
"fmt"
"strconv"
)
func main() {
b, _ := strconv.ParseBool("true")
fmt.Printf("%T, %t\n", b, b) // bool, true
f, _ := strconv.ParseFloat("3.1415", 64)
fmt.Printf("%T, %f\n", f, f) // float64, 3.141500
i, _ := strconv.ParseInt("-42", 10, 32)
fmt.Printf("%T, %d\n", i, i) // int64, -42
u, _ := strconv.ParseUint("42", 10, 16)
fmt.Printf("%T, %d\n", u, u) // uint64, 42
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
bool, true
float64, 3.141500
int64, -42
uint64, 42
bool 型への変換には ParseBool
関数を使用します。
float 型への変換には ParseFloat
関数を使用します。
1つ目の引数には変換したい文字列を指定し、2つ目の引数には変換後の bit 数を指定します(bit 数に関わらず取得されるデータ型は float64 になります)。
int 型への変換には ParseInt
関数を使用します。
1つ目の引数には変換したい文字列を指定し、2つ目の引数には変換文字列の進数を指定し、3つ目の引数には変換後の bit 数を指定します(bit 数に関わらず取得されるデータ型は int64 になります)。
uint 型への変換には ParseUint
関数を使用します。
1つ目の引数には変換したい文字列を指定し、2つ目の引数には変換文字列の進数を指定し、3つ目の引数には変換後の bit 数を指定します(bit 数に関わらず取得されるデータ型は uint64 になります)。
(2進数や 16 進数に変換する場合)
2進数や 16 進数に変換する場合は、次のようになります。
package main
import (
"fmt"
"strconv"
)
func main() {
i, err := strconv.ParseInt("ffff", 16, 64)
fmt.Printf("%T, %d\n", i, i) // int64, 65535
fmt.Println(err) // <nil>
u, err := strconv.ParseUint("11111111", 2, 64)
fmt.Printf("%T, %d\n", u, u) //uint64, 255
fmt.Println(err) // <nil>
u2, err := strconv.ParseUint("42", 2, 64)
fmt.Printf("%T, %d\n", u2, u2) // uint64, 0
fmt.Println(err) // strconv.ParseUint: parsing "42": invalid syntax
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
int64, 65535
<nil>
uint64, 255
<nil>
uint64, 0
strconv.ParseUint: parsing "42": invalid syntax
最後の例は、"42"
という数字を2進数に変換しようとしたが、0
、1
以外の数字が含まれているため、エラーが発生した場合です。
3-2. 数値から文字列への変換
3-2-1. 一般的な変換
数値から文字列への変換は、Itoa
関数を使用します(itoa とは「Integer to ASCII」の意です)。
コードを書くと、次のようになります。
package main
import (
"fmt"
"strconv"
)
func main() {
var num int = 100
var str string = strconv.Itoa(num)
fmt.Println(str) // 100
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
100
出力値の見た目は 100
ですが、文字列に変換されています。
3-2-2. 詳細な変換指定
package main
import (
"fmt"
"strconv"
)
func main() {
s1 := strconv.FormatBool(true)
fmt.Printf("%T, %s\n", s1, s1) // string, true
s2 := strconv.FormatFloat(3.1415, 'E', -1, 64)
fmt.Printf("%T, %s\n", s2, s2) // string, 3.1415E+00
s3 := strconv.FormatInt(-42, 16)
fmt.Printf("%T, %s\n", s3, s3) // string, -2a
s4 := strconv.FormatUint(42, 16)
fmt.Printf("%T, %s\n", s4, s4) // string, 2a
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
string, true
string, 3.1415E+00
string, -2a
string, 2a
bool 型から string 型への変換には FormatBool
関数を使用します。
float 型から string 型への変換には FormatFloat
関数を使用します。
1つ目の引数には変換したい実数を指定し、2つ目の引数には浮動小数点数の書式を表す指定子を指定し、3つ目の引数には変換後小数点の桁数を指定し(-1
は全て表示)、4つ目の引数には変換後の浮動小数点数の精度(bit 数)を指定します。
int 型から string 型への変換には FormatInt
関数を使用します。
1つ目の引数には変換したい整数を指定し、2つ目の引数には変換する数値の進数を指定します。
uint 型から string 型への変換には FormatUint
関数を使用します。
1つ目の引数には変換したい整数を指定し、2つ目の引数には変換する数値の進数を指定します。
詳しくは、下記のサイトが分りやすいです。
<参考>
【Go入門】strconvパッケージ ~ 文字列型と数値型・論理型の相互変換
さいごに
Go を触り始めたばかりなので間違いもあるかと思います。
お気づきのことなどありましたら、ご指摘いただけると幸いです。