1
3

オーバーフロー

Goには整数型が10個ある。

  • 符号付き整数型 : int, int8, int16, int32, int64
  • 符号なし整数型 : uint, uint8, uint16, uint32, uint64

32bitシステムの場合は32bitのint32, 64bitシステムの場合は64bitのint64を使う。
ここで、int32の最大値(math.MaxInt32)に1を足して、出力させてみます。

func main() {
	var counter int32 = math.MaxInt32
	fmt.Printf("counter = %d\n", counter)
	counter++
	fmt.Printf("counter = %d\n", counter)
}

このコードは、コンパイルエラーやパニックは起きませんが、オーバーフローが発生します。実行すると、以下のようになります。

実行結果
counter = 2147483647
counter = -2147483648

int8での最大最小を考えてみます。

10進数 2進数
0 00000000
127 01111111
-128 10000000
11111111

実際に、コードで出してみると以下のようになります。

Go Playground

func main() {
	maxInt8 := math.MaxInt8
	minInt8 := math.MinInt8
	maxUint8 := math.MaxUint8
	minUint8 := uint8(0)

	fmt.Printf("int32(10進数) : %d 〜 %d\n", minInt8, maxInt8)
	fmt.Printf("int32(2進数)  : %b 〜 %b\n", minInt8, maxInt8)

	fmt.Printf("uint32(10進数): %d 〜 %d\n", minUint8, maxUint8)
	fmt.Printf("uint32(2進数) : %b 〜 %b\n", minUint8, maxUint8)
}
実行結果
int32(10進数) : -128 〜 127
int32(2進数)  : -10000000 〜 1111111
uint32(10進数): 0 〜 255
uint32(2進数) : 0 〜 11111111

11111111の場合は2の補数となる(ビット反転して、+1)ので、オーバーフローとなります。

ちなみに、Goではコンパイル時にオーバーフローが検出できれば、コンパイルエラーとなります。

func main() {
	var counter int32 = math.MaxInt32 + 1 // コンパイルエラー
}

しかし、検知できな場合は、先ほどの例のようにオーバーフローやアンダーフローはエラーにもパニックにもならないので注意しなければならない。これらは分かりにくいバグを引き起こす。

1996年のアリアン5の打ち上げ失敗はオーバーフローが原因らしいです。
クラスターミッション

以下の場合は要注意。

  • メモリに制約があるプロジェクトで小さい整数型を使用するとき
  • 大きな数値を扱ったり変換したりするとき

オーバーフローの検知

オーバーフローを検知するためのアルゴリズムを紹介していきます。

1を加算するときの検出アルゴリズム

int8 や int16に1を加算する場合にオーバーフローを検出する場合は以下のようにする。

func Inc32(counter int32) int32 {
	if counter == math.MaxInt32 {
		panic("int32 overflow")
	}
	return counter + 1
}

Go1.17以前はMaxInt32などがなかったので、自分で定数を作成する必要がありました。

intやunitも同じようにできます。

func IncInt(counter int) int {
	if counter == math.MaxInt {
		panic("int overflow")
	}
	return counter + 1
}

func IncUint(counter uint) uint {
	if counter == math.MaxUint {
		panic("uint overflow")
	}
	return counter + 1
}

加算時の整数オーバーフローの検出アルゴリズム

2つの整数の加算によるオーバーフローはmath.MaxIntmath.MinIntを用いて検出する。

func AddInt(a, b int) int {
	if (b > 0 && a > math.MaxInt-b) || (b < 0 && a < math.MinInt-b {
		panic("int overflow")
	}
	return a + b
}

乗算時の整数のオーバーフローの検出アルゴリズム

func MultiplyInt(a, b int) int {
	if a == 0 || b == 0 {
		return 0
	}

	result := a * b
	if a == 1 || b == 1 {
		return result
	}
	if a == math.MinInt || b == math.MinInt {
		panic("integer overflow")
	}
	if result/b != a {
		panic("integer overflow")
	}
	return result
}

終わりに

Goでは大きな数を扱うパッケージとして、math/bigがある。また、適切なサイズの型を使用するのも大事で、Goのstrconv.ParseIntstrconv.ParseUintを使って、適切なbitSizeを指定することもできます。

参考文献

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3