0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GoAdvent Calendar 2022

Day 1

untyped int の思いがけなかった挙動

Last updated at Posted at 2022-12-18

これは何?

先日、untyped int の挙動で驚いたことがあったので書いておく。

Go アドベントカレンダー

の カレンダー3 の初日が空いていたので参加してみた。

コード

こんなコードがある。

go
const x = 9876543210
log.Printf("%v", x) // 32bit 環境ではコンパイルエラー

いつもは 64bit 環境で動かしていたんだけど、久々に 32bit 環境向けのバイナリを作ったらコンパイルエラーになってびっくりした。

x は untyped int。
log.Printf%v で受けるんだから、型は何でもいい。
しかし、log.Printf には untyped int のままでは渡せない。仕方ないので int に変換を試みるもオーバーフローでエラー。

v ...any に渡したいのに int にされるのが、言われてみればまあそうだよねということだけれど、思いがけなかった。

いろいろ試してみる

const int でシフト

go
const x = 9876543210
const s = int(3)
log.Printf("%v", x>>s)

これはOK。エラーにならない。
untyped のまま shift できるのだろう。

const int で割る

先ほどと実質的に同じ計算だが、

go
const x = 9876543210
const s = int(1 << 3)
log.Printf("%v", x/s) // 32bit 環境ではコンパイルエラー

こちらはエラー。untyped のままでは int での除算はできない模様。

switch に入れる

go
switch 9876543210 { // 32bit 環境ではコンパイルエラー
case 1:
	log.Println("one")
default:
	log.Println("not one")
}

switch に入れても勝手に int にされる。switch int64(9876543210) { だとエラーにならない。

case を int64 にしてみる。

go
switch 9876543210 { // 32bit 環境ではエラー
case int64(1): // mismatched types int64 and int
	log.Println("one")
default:
	log.Println("not one")
}

case の値から switch の方の型を推論することはできないらしい。

コンパイルエラーにならない場合

ここまでは幸いにしてコンパイルエラーになっていたが

と混ぜるとエラーにならなくなる。

go
const x = 100000001
s := 10
log.Println((x << s) % 1000) //=> 64bit 環境では24、32bit 環境では -80

わかりにくいし、わりと恐ろしい。

Zig ではどうなの?

「コンパイル時にしか扱えない、ビット長が確定していない値」という機能を持つ、go 以外の唯一の言語(私調べ)、Zig でどうなるか。

untyped のようなものを Printf のようなものに渡す

Zig ではこう。

zig
const x=987654321098765432109876543210;
try stdout.print("{}\n", .{x});

go だと「Printf に untyped のままでは渡せないので int に変換して渡す」という処理なのでエラーになっていた。
zig だと、comptime_int int のままで print に渡しているのでそういうトラブルは発生しない。

計算

同様にシフトと除算を試した。

zig
const x=987654321098765432109876543210;
const ys:i32 = 10;
const zs = x>>ys;
try stdout.print("{} {}\n", .{zs, @TypeOf(zs)});
// => 964506172948013117294801311 comptime_int

const yd:i32 = 1024;
const zd = x/yd; // 除算の結果が i32 に入らないのでコンパイルエラー
try stdout.print("{} {}\n", .{zd, @TypeOf(zd)});

switch〜case のようなもの

zig
const x = switch (987654321098765432109876543210) {
    i64val, i32val, i16val, i8val, i4val => 1,
    else => 0,
};
const y = switch (i64val) {
    i32val, i16val, i8val, i4val => 1,
    else => 0,
};
const z = switch (i64val) {
    9876543210 => 1,
    // 987654321098765432109876543210 => 2, // エラー: type 'i64' cannot represent integer value '987654321098765432109876543210'
    else => 0,
};

zig の場合、安全であれば型が違う値でも問題なく比較できるので、あんまりエラーにならない。
ただ、go でいうところの case にある値が switch に渡す型で表現できない場合はエラーになる。変換できないという趣旨ではなく、どうせマッチしないからエラーにしといたよ、という話だと思う。

まとめ

untyped int が int になるタイミングはときどきわかりにくくて、32bit 環境と 64bit 環境で異なる動きをすることがあるから気をつけよう。
コンパイルエラーになる場合が多いものの、そうではないパターンもある。

zig の場合、comptime_int のままで処理が進むし、そもそも処理系依存のビット長の型を使う機会が少ないのでほとんど心配なさそう。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?