整数型の詳細と整数リテラルの書き方について説明します。
ビット幅が明確に定義された整数型です。符号なしの整数型はu
を、符号ありの整数型はi
を、先頭に付けてビット幅を続けます。
代表的なビット幅を持つ整数型と、その値の範囲は、次の通りです。
型名 | ビット幅 | 最小値 | 最大値 |
---|---|---|---|
i8 | 8 | -128 | 127 |
u8 | 8 | 0 | 255 |
i16 | 16 | -32,768 | 32,767 |
u16 | 16 | 0 | 65535 |
i32 | 32 | -2,147,483,648 | 2,147,483,647 |
u32 | 32 | 0 | 4,294,967,295 |
i64 | 64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
u64 | 64 | 0 | 18,446,744,073,709,551,615 |
i128 | 128 | -(2^127) | 2^127-1 |
u128 | 128 | 0 | 2^128-1 |
上述の型に限らず、Zenでは任意のビット幅を持つ整数型が利用できます。例えば、u1
は符号なし1ビットの整数、i5
は符号あり5ビットの整数を意味します。u1
は0か1、i5
は-16から15の値だけを取ることができます。
examples/ch02-primitives/src/arbitary_bit_width.zen
const std = @import("std");
const ok = std.testing.ok;
test "arbitary bit width" {
var v1: u1 = 0;
ok(v1 == 0);
v1 = 1;
ok(v1 == 1);
// Compile error: integer value 2 cannot be implicitly casted to type 'u1'
// v1 = 2;
var v2: i5 = -16;
ok(v2 == -16);
v2 = 15;
ok(v2 == 15);
// Compile error: integer value -17 cannot be implicitly casted to type 'i5'
// v2 = -17;
}
ノート: 整数型が取れる最大ビット幅は65535ビットです。
Zenでは明確にビット幅が定義された整数型の他、ターゲット環境のアドレスビット幅に依存する整数型があります。
型名 | 32ビット環境 | 64ビット環境 |
---|---|---|
isize | 32ビット | 64ビット |
usize | 32ビット | 64ビット |
これら2つの整数型は、ターゲットとする環境のメモリアドレスのビット幅に依存して、ビット幅が変わります。isize
は符号あり、usize
は符号なしです。
usize
は配列のインデックスなど、幅広く利用されます。
Zenには、コンパイル時のみ利用できる整数型comptime_int
があります。
comptime_int
型はビット幅による値の制限がありません。そのため、非常に大きな数値を表現することができます。一方、ビット幅が定義されていないため、実行時に値を参照するためには、u32
やusize
といった他の整数型に変換する必要があります。
comptime_int
型の変数は、コンパイル時に計算可能である必要があります。実行時に値が決まったり、値が変化する変数は、comptime_int
型になることはできません。
comptime_int
型は、他の整数型 (u32
やusize
) に対して暗黙の型変換が働きます。
整数リテラルとは、整数型に対してプログラム内に直接記述した数値を意味します。
整数リテラルは、デフォルトではcomptime_int
型になります。
examples/ch02-primitives/src/integers.zen:1:7
const std = @import("std");
const ok = std.testing.ok;
test "integer literal" {
// 整数リテラルはcomptime_int型
const decimal = 12345678;
ok(@TypeOf(decimal) == comptime_int);
comptime_int
型は、ビット幅の情報を持ちません。そのため、コンパイル時に計算可能であれば、どのような範囲の値も取ることができます。
examples/ch02-primitives/src/integers.zen:9:10
const big_int = 2^200; // u128の最大値より大きい数値もOK
ok(big_int == 2^200);
ビット幅を指定した整数リテラルを利用する場合は、@to(型名, 整数リテラル)
の構文を利用します。
examples/ch02-primitives/src/integers.zen:12:15
const decimal_u32 = @to(u32, 12345678); // 型をu32に指定
ok(decimal_u32 == 12345678);
ok(@TypeOf(decimal_u32) == u32);
ok(@sizeOf(@TypeOf(decimal_u32)) == 4);
コンパイル時に計算できない値は、comptime_int
型を取れないため、具体的な型を明示する必要があります。型は、リテラルに付けることもできますし、変数に付けることもできます。リテラルに付ける場合は、 @to
による型変換が必要です。
examples/ch02-primitives/src/integers.zen:17:21
// 実行時に変化する値は、`comptime_int`型になれない
var v1 = @to(u32, 1);
var v2: u32 = 1;
// Compile error
// var v1 = 1;
整数リテラルの先頭に、0x
, 0o
, 0b
を付けることで、それぞれ16進数、8進数、2進数で整数リテラルの値を書くことができます。
examples/ch02-primitives/src/integers.zen:23:36
// 16進数リテラル
const hex = 0xbc614e;
ok(hex == 12345678);
// A-Fは大文字も可
const HEX = 0xBC614E;
ok(HEX == 12345678);
// 8進数リテラル
const octal = 0o57060516;
ok(octal == 12345678);
// 2進数リテラル
const binary = 0b101111000110000101001110;
ok(binary == 12345678);
10進数の整数リテラルを 0
から始めることはできません。
C 言語等では 0177
のような整数リテラルは 8 進数と解釈されますが、
Zen 言語ではこのような書き方はエラーとなります。
Zen 言語の古いバージョンでは、
0177
は 10 進数の 117 と解釈されていました。 8 進数との混同を避けるため、現在ではこのような表記は禁止されています。
整数リテラルを読みやすくするために、アンダースコア_
を間に入れることができます。
examples/ch02-primitives/src/integers.zen:38:40
// `_`で可読性をあげることもできる
const underscore = 1234_5678;
ok(underscore == 12345678);
ASCII文字に対応する文字コードを得られます。型は、comptime_int
です。
examples/ch02-primitives/src/integers.zen:43:47
test "ascii code literal" {
const a = 'A';
ok(a == 65);
ok(@TypeOf(a) == comptime_int);
}
Zenの標準ライブラリstd.math
のminInt
とmaxInt
を使用して、整数型の最小値と最大値を取得することができます。
examples/ch02-primitives/src/integers.zen:49:56
test "max min of int" {
const math = std.math;
ok(math.minInt(u8) == 0);
ok(math.maxInt(u8) == 255);
ok(math.minInt(i5) == -16);
ok(math.maxInt(i5) == 15);
}
Zenでは、安全な場合のみ、整数型同士の暗黙の型変換が行われます。わかりやすい例は、より大きな整数型への変換です。
examples/ch02-primitives/src/integers.zen:58:67
test "implicit cast" {
// より大きな整数型への暗黙の型変換
const v1: u8 = 255;
const v2: u16 = v1;
ok(v2 == 255);
// より大きな符号あり整数型への暗黙の型変換
const v3: u8 = 255;
const v4: i16 = v3;
ok(v4 == 255);
コンパイル時に安全性が保証される場合、より小さな整数型への変換も可能です。
examples/ch02-primitives/src/integers.zen:69:73
// より小さな整数型への変換だが、コンパイル時に値が`u8`に収まることが
// わかるため、暗黙の型変換可能
const v5: u16 = 255;
const v6: u8 = v5;
ok(v6 == 255);
下のコードは、v7
が実行時変数となるため、コンパイルエラーになります。
var v7: u16 = 255;
var v8: u8 = v7;
安全性を保証できない整数型の型変換を行うには、2つの方法があります。組込み関数の@truncate
を使用する方法と、組込み関数の@intCast
を使用する方法、です。
ノート:
@
から始まる関数はコンパイラ組込みの特別な関数です。詳しくは7章 組込み関数で説明します。
2つの方法の違いは、@truncate
が単純に上位ビットを切り捨てるのに対して、@intCast
は、安全性保護付き未定義動作が発生する可能性がある点です。
まずは@truncate
を使う方法を説明します。
examples/ch02-primitives/src/integers.zen:76:85
test "truncate" {
var v1: u16 = 255;
var v2: u8 = @truncate(u8, v1);
ok(v2 == 255);
var v3: u16 = 256;
var v4: u8 = @truncate(u8, v3);
// 0x0100 => 0x00
ok(v4 == 0);
}
@truncate
は、@truncate(ターゲットの型、値)
のように利用します。2つめのok(v4 == 0)
から、u16
の0x0100
をu8
に@truncate
した結果、上位8ビットが切り捨てられ、u8
の0x00
に値が変化していることがわかります。
次に@intCast
を使う方法です。
examples/ch02-primitives/src/integers.zen:87:91
test "intCast" {
var v1: u16 = 255;
var v2: u8 = @intCast(u8, v1);
ok(v2 == 255);
}
255
は安全にu8
に変換できるため、上のコードは@truncate
と同様に動作します。一方、次のコードは、実行時に安全性保護付き未定義動作が発生し、プログラムが停止します。
var v3: u16 = 256;
var v4: u8 = @intCast(u8, v3);
@intCast
では、変換する値が変換先の型の範囲内に収まっているかどうか、を実行時に検査します。検査の結果、値が型の範囲内であれば変換が行われ、そうでなければ安全性保護付き未定義動作が発生します。
整数型同士を計算すると、計算結果が整数型の範囲を超え、オーバーフローが発生する場合があります。例えば、u8
では0から255までの値を表現できますが、255+1
の計算を行うと、その結果の256
はu8
では表現できません。
Zenでは、このようなオーバーフローが発生した場合の動作を選択することができます。
まず、次のコードは実行時に安全性保護付き未定義動作が発生し、プログラムが停止します。
var v1: u8 = std.math.maxInt(u8);
var v2: u8 = v1 + 1; // オーバーフローで安全性保護付き未定義動作発生
このような安全性保護付き未定義動作を防ぐためには、主に2つの対策があります。ラップアラウンド演算子を使う方法と、オーバーフロー検出機能付き組込み関数を使う方法です。
まず、ラップアラウンド演算子を使用する方法です。ラップアラウンドとは、その整数型で表現可能な数値の範囲内で、値を循環させることです。加算であれば、+
演算子の代わりに+%
演算子を使用します。
examples/ch02-primitives/src/integers.zen:93:97
test "wraparound" {
var v1: u8 = std.math.maxInt(u8);
var v2: u8 = v1 +% 1;
ok(v2 == 0);
}
上の例では、u8
の最大値に1
をラップアラウンド加算した結果は、0
になっています。これは、u8
で表現可能な255
を越えた次の値が、0
に循環していることを意味します。
演算の結果、ラップアラウンドすれば良い場合は、こちらのラップアラウンド演算子を使う方法を採用します。
もう1つの方法は、オーバーフロー検出機能付き組込み関数を使う方法です。加算であれば、@addWithOverflow
を使用します。
examples/ch02-primitives/src/integers.zen:99:107
test "withOverflow" {
var v1: u8 = std.math.maxInt(u8);
var v2: u8 = undefined;
const result = @addWithOverflow(u8, v1, 1, &mut v2);
// `true`はオーバーフローが発生したことを意味する
ok(result == true);
// オーバーフロー発生時の値
ok(v2 == 0);
}
@addWithOverflow
は、@addWithOverflow(ターゲットの型, 値1, 値2, 結果を格納するポインタ) bool
のように利用します。オーバーフローが発生した場合、戻り値はtrue
が返ってきます。また、オーバーフローが発生した結果の値が、第4引数で与えたポインタに格納されます。
☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.