🐭

Zigの整数の型変換の組み込み関数を整理しよう

2023/01/16に公開

前回の記事でZigの型変換に使う組み込み関数がたくさんでてきた。
いったん整理しよう。今回は整数のみに注目する。

@truncate

@truncate
サイズが小さくなる方に変換するときに使う。
実行時に行われる。
上位のbitが切り取られるので、数値は変化することがある。

@as

@as
コンパイル時に行われる型変換。
サイズが小さくなる方向の変換はコンパイルエラーになる。
変数の場合は自明なので、数値リテラルに明示的に型を与える場面で使われる。
(数値リテラルには型が無いため。これはGo言語と同じ)
(数値リテラルはcomptime_intという独特の型になっている)

@intCast

@intCast
実行時に行われる型変換。しかしコンパイル時に定数となるものに使われた場合は@asと同じになる。
サイズが小さくなる方向の変換で数値の意味が変わってしまう(変更後のサイズでは表現できない)場合は異常終了となる。
言い方を変えると、@truncateと同様に上位bitの切り取りを行って、切り取ったbit全てが符号ビットと一致しない場合に異常終了する。

@bitCast (2023/01/17修正)

@bitCast
bit幅が同じもの同士での変換。
例えば、f32 -> u32, i32 -> u32 など。

数値を保持するbit列はそのままで、コンパイラがその解釈のみ変更する。

b.zig
const std = @import("std");
const expect = std.testing.expect;

fn func(xa: []const i32) void {
    for (xa) |x| {
        std.debug.print("x = 0x{0x}, {0d} -> 0x{1x}, {1d}\n", .{x, @bitCast(u32, x)});
    }    
}

test "bitCast i32 -> u32" {
    std.debug.print("\n", .{});
    const xa = [_]i32{3, 2, 1, 0, -1, -2, -3};
    func(&xa);
}

fn func2(fa: []const f32) void {
    for (fa) |f| {
        std.debug.print("f = {d} -> 0x{x:0>8}\n", .{f, @bitCast(u32, f)});
    }
}

test "bitCast f32 -> u32" {
    std.debug.print("\n", .{});
    const fa = [_]f32{1.0, 0.0, -1.0, 1.0e-6, 0.15625};
    func2(&fa);
}
$ zig test b.zig
Test [1/2] test.bitCast i32 -> u32... 
x = 0x3, 3 -> 0x3, 3
x = 0x2, 2 -> 0x2, 2
x = 0x1, 1 -> 0x1, 1
x = 0x0, 0 -> 0x0, 0
x = 0x-1, -1 -> 0xffffffff, 4294967295
x = 0x-2, -2 -> 0xfffffffe, 4294967294
x = 0x-3, -3 -> 0xfffffffd, 4294967293
Test [2/2] test.bitCast f32 -> u32... 
f = 1 -> 0x3f800000
f = 0 -> 0x00000000
f = -1 -> 0xbf800000
f = 0.0000009999999974752427 -> 0x358637bd
f = 0.15625 -> 0x3e200000
All 2 tests passed.

関連

https://zenn.dev/tetsu_koba/articles/0e91b69ff72ae0
https://zenn.dev/tetsu_koba/articles/c039b865114b93

Discussion